使用 PhantomJS 抓取带有动态组合下拉框的 ASP.NET 站点
Scrape ASP.NET site with dynamic combo dropdown box with PhantomJS
我正在尝试使用 PhantomJS v1.9.8 抓取用 ASP.NET 编写的带有 7 个动态组合下拉框的 this 页面。
我的JS如下:
var page = require('webpage').create();
console.log('User agent is ' + page.settings.userAgent);
page.settings.userAgent = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.3 Safari/533.2';
page.open('http://www.etcfinance.com.hk/online_appraise.aspx', function(status) {
page.injectJs("http://code.jquery.com/jquery-latest.js", function() {
page.evaluate(function() {
$("#ddlArea").val('香港');
__doPostBack('ddlArea', '');
setTimeout(function() {
console.log('Zone: ' + $('#ddlZone').val());
}, 1000);
});
phantom.exit();
});
});
输出挂在:
User agent is Mozilla/5.0 (Macintosh; PPC Mac OS X) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.8 Safari/534.34
但之后没有继续。我怎样才能 select 所有那些具有所需值的组合下拉框?
HTML的相关部分如下:
<table xwidth="100%" width="460" bgcolor="#E0F3FF" border="0" cellpadding="3" cellspacing="0" class="content">
<tbody><tr height="20"><td></td></tr>
<tr class="insidecontent">
<td style="Padding-Left:20px;Padding-Right:20px;">
<div align="left"> 區域 : </div>
</td>
<td valign="top">
<select name="ddlArea" onchange="javascript:setTimeout('__doPostBack(\'ddlArea\',\'\')', 0)" id="ddlArea" class="textbox" style="width:29em">
<option selected="selected" value="">請選擇區域</option>
<option value="香港">香港</option>
<option value="九龍">九龍</option>
<option value="新界/離島">新界/離島</option>
</select>
</td>
</tr>
<tr class="insidecontent">
<td style="Padding-Left:20px;Padding-Right:20px;">
<div align="left"> 分區 : </div>
</td>
<td valign="top">
<select name="ddlZone" onchange="javascript:setTimeout('__doPostBack(\'ddlZone\',\'\')', 0)" id="ddlZone" class="textbox" style="width:29em">
</select>
</td>
</tr>
<tr class="insidecontent">
<td style="Padding-Left:20px;Padding-Right:20px;">
<div align="left"> 屋苑名稱 : </div>
</td>
<td valign="top">
<select name="ddlestate" onchange="javascript:setTimeout('__doPostBack(\'ddlestate\',\'\')', 0)" id="ddlestate" class="textbox" style="width:29em">
</select>
</td>
</tr>
<tr class="insidecontent">
<td style="Padding-Left:20px;Padding-Right:20px;">
<div align="left"> 座數/座名 : </div>
</td>
<td valign="top">
<select name="ddlblock" onchange="javascript:setTimeout('__doPostBack(\'ddlblock\',\'\')', 0)" id="ddlblock" class="textbox" style="width:29em">
</select>
</td>
</tr>
<tr class="insidecontent">
<td style="Padding-Left:20px;Padding-Right:20px;">
<div align="left"> 層數 : </div>
</td>
<td valign="top">
<select name="ddlfloor" onchange="javascript:setTimeout('__doPostBack(\'ddlfloor\',\'\')', 0)" id="ddlfloor" class="textbox" style="width:29em">
</select>
</td>
</tr>
<tr class="insidecontent">
<td style="Padding-Left:20px;Padding-Right:20px;">
<div align="left" id="div1"> 室 : </div>
</td>
<td valign="top">
<div id="div2">
<select name="ddlflat" id="ddlflat" class="textbox" style="width:29em">
</select>
</div>
</td>
</tr>
<tr height="20"><td></td></tr>
</tbody></table>
注意:我知道上面的HTML全是错误。
此外,我使用 page.injectJS
而不是 page.includeJS
的原因是后一个函数会导致以下错误:
Unsafe JavaScript attempt to access frame with URL about:blank from frame with URL file://parse.js. Domains, protocols and ports must match.
page.injectJs
没有回调,只能注入本地文件。您想要执行的任何事情实际上都没有执行。您需要使用 page.includeJs
来包含远程脚本。
因此,您可以将 jQuery 下载到本地目录并使用 page.injectJs
(最简单的解决方案),或者尝试使其与远程脚本和 page.includeJs
一起使用。这将包括 运行 命令行选项,例如 --web-security=false
和 --local-to-remote-url-access=true
.
顺便说一句,jquery-latest.js 永远固定为 1.11.1。如果您想要更新的 jQuery,请使用实际版本号。
您的脚本还有另一个问题,您在使用它后会遇到这个问题。你exit
太早了。 setTimeout
中断阻塞执行流程,page.evaluate
立即完成。在调用 setTimeout
回调之前调用 exit
。解决方法:
page.evaluate(function() {
$("#ddlArea").val('香港');
__doPostBack('ddlArea', '');
});
setTimeout(function() {
page.evaluate(function() {
console.log('Zone: ' + $('#ddlZone').val());
});
phantom.exit();
}, 1000);
这样更好,但您仍然不会在控制台上看到任何内容,因为您还需要注册到 page.onConsoleMessage
事件。
链接版本:
var selects = [
['ddlArea', '香港'],
['ddlZone', '...'],
...
];
selects.forEach(function(sel, i){
setTimeout(function() {
page.evaluate(function(sel) {
$("#"+sel[0]).val(sel[1]);
__doPostBack(sel[0], '');
}, sel);
}, i * 1000);
});
setTimeout(function() {
phantom.exit();
}, 1000 * selects.length);
更好的版本是使用实际点击次数并使用 waitFor
to wait until the next select is populated in conjunction with async.js。
我正在尝试使用 PhantomJS v1.9.8 抓取用 ASP.NET 编写的带有 7 个动态组合下拉框的 this 页面。
我的JS如下:
var page = require('webpage').create();
console.log('User agent is ' + page.settings.userAgent);
page.settings.userAgent = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.3 Safari/533.2';
page.open('http://www.etcfinance.com.hk/online_appraise.aspx', function(status) {
page.injectJs("http://code.jquery.com/jquery-latest.js", function() {
page.evaluate(function() {
$("#ddlArea").val('香港');
__doPostBack('ddlArea', '');
setTimeout(function() {
console.log('Zone: ' + $('#ddlZone').val());
}, 1000);
});
phantom.exit();
});
});
输出挂在:
User agent is Mozilla/5.0 (Macintosh; PPC Mac OS X) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.8 Safari/534.34
但之后没有继续。我怎样才能 select 所有那些具有所需值的组合下拉框?
HTML的相关部分如下:
<table xwidth="100%" width="460" bgcolor="#E0F3FF" border="0" cellpadding="3" cellspacing="0" class="content">
<tbody><tr height="20"><td></td></tr>
<tr class="insidecontent">
<td style="Padding-Left:20px;Padding-Right:20px;">
<div align="left"> 區域 : </div>
</td>
<td valign="top">
<select name="ddlArea" onchange="javascript:setTimeout('__doPostBack(\'ddlArea\',\'\')', 0)" id="ddlArea" class="textbox" style="width:29em">
<option selected="selected" value="">請選擇區域</option>
<option value="香港">香港</option>
<option value="九龍">九龍</option>
<option value="新界/離島">新界/離島</option>
</select>
</td>
</tr>
<tr class="insidecontent">
<td style="Padding-Left:20px;Padding-Right:20px;">
<div align="left"> 分區 : </div>
</td>
<td valign="top">
<select name="ddlZone" onchange="javascript:setTimeout('__doPostBack(\'ddlZone\',\'\')', 0)" id="ddlZone" class="textbox" style="width:29em">
</select>
</td>
</tr>
<tr class="insidecontent">
<td style="Padding-Left:20px;Padding-Right:20px;">
<div align="left"> 屋苑名稱 : </div>
</td>
<td valign="top">
<select name="ddlestate" onchange="javascript:setTimeout('__doPostBack(\'ddlestate\',\'\')', 0)" id="ddlestate" class="textbox" style="width:29em">
</select>
</td>
</tr>
<tr class="insidecontent">
<td style="Padding-Left:20px;Padding-Right:20px;">
<div align="left"> 座數/座名 : </div>
</td>
<td valign="top">
<select name="ddlblock" onchange="javascript:setTimeout('__doPostBack(\'ddlblock\',\'\')', 0)" id="ddlblock" class="textbox" style="width:29em">
</select>
</td>
</tr>
<tr class="insidecontent">
<td style="Padding-Left:20px;Padding-Right:20px;">
<div align="left"> 層數 : </div>
</td>
<td valign="top">
<select name="ddlfloor" onchange="javascript:setTimeout('__doPostBack(\'ddlfloor\',\'\')', 0)" id="ddlfloor" class="textbox" style="width:29em">
</select>
</td>
</tr>
<tr class="insidecontent">
<td style="Padding-Left:20px;Padding-Right:20px;">
<div align="left" id="div1"> 室 : </div>
</td>
<td valign="top">
<div id="div2">
<select name="ddlflat" id="ddlflat" class="textbox" style="width:29em">
</select>
</div>
</td>
</tr>
<tr height="20"><td></td></tr>
</tbody></table>
注意:我知道上面的HTML全是错误。
此外,我使用 page.injectJS
而不是 page.includeJS
的原因是后一个函数会导致以下错误:
Unsafe JavaScript attempt to access frame with URL about:blank from frame with URL file://parse.js. Domains, protocols and ports must match.
page.injectJs
没有回调,只能注入本地文件。您想要执行的任何事情实际上都没有执行。您需要使用 page.includeJs
来包含远程脚本。
因此,您可以将 jQuery 下载到本地目录并使用 page.injectJs
(最简单的解决方案),或者尝试使其与远程脚本和 page.includeJs
一起使用。这将包括 运行 命令行选项,例如 --web-security=false
和 --local-to-remote-url-access=true
.
顺便说一句,jquery-latest.js 永远固定为 1.11.1。如果您想要更新的 jQuery,请使用实际版本号。
您的脚本还有另一个问题,您在使用它后会遇到这个问题。你exit
太早了。 setTimeout
中断阻塞执行流程,page.evaluate
立即完成。在调用 setTimeout
回调之前调用 exit
。解决方法:
page.evaluate(function() {
$("#ddlArea").val('香港');
__doPostBack('ddlArea', '');
});
setTimeout(function() {
page.evaluate(function() {
console.log('Zone: ' + $('#ddlZone').val());
});
phantom.exit();
}, 1000);
这样更好,但您仍然不会在控制台上看到任何内容,因为您还需要注册到 page.onConsoleMessage
事件。
链接版本:
var selects = [
['ddlArea', '香港'],
['ddlZone', '...'],
...
];
selects.forEach(function(sel, i){
setTimeout(function() {
page.evaluate(function(sel) {
$("#"+sel[0]).val(sel[1]);
__doPostBack(sel[0], '');
}, sel);
}, i * 1000);
});
setTimeout(function() {
phantom.exit();
}, 1000 * selects.length);
更好的版本是使用实际点击次数并使用 waitFor
to wait until the next select is populated in conjunction with async.js。