简而言之,我正在尝试使用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="✓"
/><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);
不工作..需要检索数组输出$m
preg_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>
是您的正则表达式。