正则表达式 (preg_match_all) 从隐藏登录表单中检索真实性令牌



简而言之,我正在尝试使用CURL登录电子商务平台Bonanza,以便我可以自动打印进来的新订单。

我在GitHub上搜索,在这里找到了Twitter的自动登录脚本,它看起来与Bonanza的运作方式非常相似。

我尝试首先执行我的 curl 请求的登录页面位于此处

它包括一个表单,该表单发布以下变量以登录

utf8: ✓
authenticity_token: 0tMPrfH0+Tt7z05jxu61pN10RveVp6o0dsfgf=4cS6g7kyeMsztpDmWj2P1ZYasfdf3QjNl/og==
username: myusername
password: mypassword
commit: Log in

查看表单的源,您可以看到我需要检索的令牌的name="authenticity_token"value=

<form class="user_session_form"
action="https://www.bonanza.com/sessions" accept-charset="UTF-8"
method="post"><input name="utf8" type="hidden" value="&#x2713;"
/><input type="hidden" name="authenticity_token" value="siKgYUtSqTs8DHCXmj8gbV6Gp3L7gaQ9C/B0rLM9/V94+FnSxTb+x6vXADSFROCxxMLB3RAqOMeL/IJQADq6dk8A=="
/>

如前所述,这似乎与 Twitter 登录脚本的工作方式非常相似,因为它会找到身份验证令牌,并使用传递的变量发出 POST 请求以https://twitter.com/sessions成功登录。

推特脚本使用此preg_match_all函数获取身份验证令牌

function ara($ilk, $son, $text) {
@preg_match_all('/' . preg_quote($ilk, '/') .
'(.*?)'. preg_quote($son, '/').'/i', $text, $m);
return @$m[1];
}

以下是使用该函数获取身份验证令牌的方式...

$baslik = ara("<input type="hidden" value=", "" name="authenticity_token">", $html);

注意 ($html( 是登录页面的 curl 可执行文件。

因此,再次总结一下,https://www.bonanza.com/home/login 需要以下formdata来登录:

utf8=%E2%9C%93&authenticity_token=SFrh%2FvFx7%2BH%2FA3kMQ2WEfZ23423AlbtP3bfT%2FaxQw7CwlgeUz5BBTMgtU7eHb%2BqyTnxs1TC30h64mT98mvA%3D%3D&username=myusername&password=mypassword&commit=Log+in

使用这些变量进行 POST 以 https://www.bonanza.com/sessions 成功登录。

我正在尝试尽我所能修改推特脚本,这是我到目前为止所拥有的:

$username = "example@stackoverflow.com";
$password = "password"; 
$ch = curl_init();
$rand = rand(1,99999);
$cookie =  $_SERVER['DOCUMENT_ROOT'] . "/cookie-$rand.txt";
$sTarget = "https://www.bonanza.com/home/login";
curl_setopt($ch, CURLOPT_URL, $sTarget);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie);
curl_setopt($ch, CURLOPT_REFERER, "https://www.bonanza.com/home/login");
$html = curl_exec($ch);
preg_match_all('/' . preg_quote("<input type="hidden" value=", '/') .
'(.*?)'. preg_quote("" name="authenticity_token">", '/').'/i', $html, $m);

不工作..需要检索数组输出$mpreg_match_all$authtoken

$sPost = "utf8=%E2%9C%93&authenticity_token=$authtoken&username=$username&password=$password&commit=Log+in";
$sTarget = "https://www.bonanza.com/sessions";
curl_setopt($ch, CURLOPT_URL, $sTarget);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $sPost);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-type: application/x-www-form-urlencoded"));
curl_exec($ch);

我尝试调试并查看preg_match_all调用中是否有任何$m输出,但输出是一个空数组

Array
(
[0] => Array
(
)
[1] => Array
(
)
)

如何修改我的preg_match_all调用(或其他方法(以检索成功提交表单登录所需的authenticity token,以及以这种方式通过 CURL 以编程方式登录还有什么我应该注意的吗?

我会尽量不使用正则表达式,而是使用PHP的标准DOMDocument XML操作库从DOM中提取它:

$doc = new DOMDocument();
$doc->loadHTML($html);
$xpath = new DOMXPath($doc);
$query = '//form[contains(@class, "user_session_form")]/input[contains(@name, "authenticity_token")]';
$inputs= $xpath->query($query);
foreach($inputs as $input) {
echo $input->getAttribute('value');
}

$query变量是一个 xpath 查询。

您可以使用此正则表达式来获取真实性令牌。
它出现在捕获组 4 中。

属性值的顺序无关紧要,这会将它们
放在有效输入标记中的任何位置。

(?s)<input(?=s)(?=(?:[^>"']|"[^"]*"|'[^']*')*?stypes*=s*(?:(['"])s*hiddens*1))(?=(?:[^>"']|"[^"]*"|'[^']*')*?snames*=s*(?:(['"])s*authenticity_tokens*2))(?=(?:[^>"']|"[^"]*"|'[^']*')*?svalues*=s*(?:(['"])s*(.*?)s*3))s+(?:"[Ss]*?"|'[Ss]*?'|[^>]*?)+>

https://regex101.com/r/NCjFxc/1

引用

单,波浪号作为正则表达式分隔符:
'~(?s)<input(?=s)(?=(?:[^>"']|"[^"]*"|'[^']*')*?stypes*=s*(?:(['"])s*hiddens*1))(?=(?:[^>"']|"[^"]*"|'[^']*')*?snames*=s*(?:(['"])s*authenticity_tokens*2))(?=(?:[^>"']|"[^"]*"|'[^']*')*?svalues*=s*(?:(['"])s*(.*?)s*3))s+(?:"[Ss]*?"|'[Ss]*?'|[^>]*?)+>~'

Double, Tilde as regex delimiter:
"~(?s)<input(?=\s)(?=(?:[^>"']|"[^"]*"|'[^']*')*?\stype\s*=\s*(?:(['"])\s*hidden\s*\1))(?=(?:[^>"']|"[^"]*"|'[^']*')*?\sname\s*=\s*(?:(['"])\s*authenticity_token\s*\2))(?=(?:[^>"']|"[^"]*"|'[^']*')*?\svalue\s*=\s*(?:(['"])\s*(.*?)\s*\3))\s+(?:"[\S\s]*?"|'[\S\s]*?'|[^>]*?)+>~"

可读版本

(?s)
# Begin Input tag
< input                # input tag
(?= s )
(?=                    # Type Hidden (a pseudo atomic group)
(?: [^>"'] | " [^"]* " | ' [^']* ' )*?
s type s* = s*      # Type
(?:
( ['"] )               # (1), Quote
s* hidden s*         # Hidden
1 
)
)
(?=                    # Name authenticity_token
(?: [^>"'] | " [^"]* " | ' [^']* ' )*?
s name s* = s*      # Name
(?:
( ['"] )               # (2), Quote
s* authenticity_token s*   # "Authenticity Token"
2 
)
)
(?=                    # Value of authenticity_token
(?: [^>"'] | " [^"]* " | ' [^']* ' )*?
s value s* = s*     # Value
(?:
( ['"] )               # (3), Quote
s* 
( .*? )                # (4), Authenticity Token Value 
s* 
3 
)
)
# Have the Authenticity Token, just match the rest of tag
s+ 
(?: " [Ss]*? " | ' [Ss]*? ' | [^>]*? )+
>                      # End tag

您正在尝试匹配

<input type="hidden" name="authenticity_token" value="{$token}"/>

您的模式是:

"/<input type="hidden" value=(.*?)" name="authenticity_token">/i"

你看到了吗?

它应该是:"<input type="hidden" name="authenticity_token" value="([^"]+)"/>"

编辑:如果能够在不受特定属性顺序限制的情况下进行匹配很重要:

<input (?:(?:type="hidden"|name="authenticity_token"|value="([^"]+)"|(?!(?:name|type|value))[^=]+="[^"]+")s*)+

将在不捕获的情况下使用未命名为"类型"或"名称"的任何属性及其值,它需要以type="hidden"name="authenticity_token"的形式存在,如果遇到属性"值",则其值将在捕获组 1 中捕获。

编辑 2:preg_match(( 和 preg_replace(( 等在模式的开头和开头都需要分隔符:http://php.net/manual/en/regexp.reference.delimiters.php

因此,您只需像这样封装表达式:"/<expression>/""~<expression>~"<expression>是您的正则表达式。

最新更新