【问题标题】:Best way to crawl a page with multiple redirects抓取具有多个重定向的页面的最佳方法
【发布时间】:2014-06-11 20:33:52
【问题描述】:

我想爬取 NCBI 网站并通过此链接发送蛋白质局部比对请求: http://blast.ncbi.nlm.nih.gov/Blast.cgi?PROGRAM=blastp&BLAST_PROGRAMS=blastp&PAGE_TYPE=BlastSearch

我想知道我是否能够使用 PHP 向该地址提交一个发布请求并获得新页面中的结果。还有一个问题,在显示最终结果之前,页面会经历多次重定向 - 您可以使用以下进入文本区域的输入来测试这种情况:

MHSSIVLATVLFVAIASASKTRELCMKSLEHAKVGTSKEAKQDGIDLYKHMFEHYPAMKKYFKHRENYTP
ADVQKDPFFIKQGQNILLACHVLCATYDDRETFDAYVGELMARHERDHVKVPNDVWNHFWEHFIEFLGSK
TTLDEPTKHAWQEIGKEFSHEISHHGRHSVRDHCMNSLEYIAIGDKEHQKQNGIDLYKHMFEHYPHMRKA
FKGRENFTKEDVQKDAFFVNKDTRFCWPFVCCDSSYDDEPTFDYFVDALMDRHIKDDIHLPQEQWHEFWK
LFAEYLNEKSHQHLTEAEKHAWSTIGEDFAHEADKHAKAEKDHHEGEHKEEHH

这是我的尝试:

$link = 'http://blast.ncbi.nlm.nih.gov/Blast.cgi?
PROGRAM=blastp&BLAST_PROGRAMS=blastp&PAGE_TYPE=BlastSearch';

$request = array(
    'http' => array(
        'method' => 'POST',
        'content' => http_build_query(array(
            'QUERY' => $aaText
            )
        ),
    )
);

$context = stream_context_create($request);
$html = file_get_html($link, false, $context);
echo $html;

这段代码让我获得了初始页面,就好像没有进行过 POST 一样。 谢谢


更新

我尝试了以下建议之一 - Goutte。

这是我的新代码:

require_once 'goutte.phar';

use Goutte\Client;

$client = new Client();

$crawler = $client->request('GET', $link);

$form = $crawler->selectButton('b1')->form();

$crawler = $client->submit($form, array('QUERY' => $aaTest));

echo $crawler->html();

变量$aaTest 是我上面给出的蛋白质序列。好的部分是:它发布,让我进入新页面,但不遵循所有重定向。我怎样才能让它遵循所有的重定向?

【问题讨论】:

  • 请解释为什么投反对票,而 3 关闭标志。
  • 您的抓取是使用“Blast”还是“旧程序”完成的?如果这是问题所在,我们能否查看您遇到问题的代码(在上述任一类别中)?
  • 它是用 php 完成的。我正在附上我的尝试。
  • 我怀疑这个问题会结束,可能是因为这里不知道 Blast,因此这个问题(在其第一个版本中)听起来像是请求第三方支持。而且问题中没有代码,所以无法回答。您现在有了代码,所以如果问题关闭,可以重新打开。
  • 现在投票重新开放。同时,您可能希望使用支持重定向的 HTTP 库。看看 Goutte,它在内部使用 Guzzle - 它非常灵活且易于使用。

标签: php web-crawler goutte ncbi


【解决方案1】:

我应该认为这个网站非常容易抓取。要了解发生了什么,请关闭浏览器中的 JavaScript 并尝试浏览该站点(为此,我使用 Firebug 中的 Disable->Disable JavaScript 菜单,这是一个 Firefox 插件)。

如果您转到第一个链接并粘贴您的字符串,您会在 POST 操作中获得一个表单,该表单实际上表明您的搜索正在进行中。它看起来像这样:

职位名称:蛋白质序列(333 个字母)

请求 ID:NR8ZP8E1071

由于对这个屏幕没有太多兴趣,我假设你不想从这里刮掉 - 但这实际上是你目前正在做的事情。

接下来发生的是一段 JavaScript 提交一个隐藏的表单,使用以下代码:

<SCRIPT LANGUAGE="JavaScript">
setTimeout('document.forms[0].submit();',1000);
</SCRIPT>

我的猜测是,在重负载时,这里的延迟(目前设置为 1000 毫秒,即 1 秒)会增加一点。隐藏的表单如下所示:

<form action="Blast.cgi" enctype="application/x-www-form-urlencoded" method="post" name="RequestFormat" id="RequestFormat&quot;">               
<input name="CMD" value="Get" type="hidden">
<input name="ALIGNMENTS" value="100" type="hidden">
<input name="ALIGNMENT_VIEW" value="Pairwise" type="hidden">
<input name="BLAST_PROGRAMS" value="blastp" type="hidden">
<input name="CDD_RID" value="data_cache_seq:180192" type="hidden">
<input name="CDD_SEARCH" value="on" type="hidden">
<input name="CDD_SEARCH_STATE" value="4" type="hidden">
<input name="CLIENT" value="web" type="hidden">
<input name="COMPOSITION_BASED_STATISTICS" value="2" type="hidden">
<input name="CONFIG_DESCR" value="2,3,4,5,6,7,8" type="hidden">
<input name="DATABASE" value="nr" type="hidden">
<input name="DESCRIPTIONS" value="100" type="hidden">
<input name="EQ_OP" value="AND" type="hidden">
<input name="EXPECT" value="10" type="hidden">
<input name="FILTER" value="F" type="hidden">
<input name="FORMAT_NUM_ORG" value="1" type="hidden">
<input name="FORMAT_OBJECT" value="Alignment" type="hidden">
<input name="FORMAT_TYPE" value="HTML" type="hidden">
<input name="FULL_DBNAME" value="nr" type="hidden">
<input name="GAPCOSTS" value="11 1" type="hidden">
<input name="GET_SEQUENCE" value="on" type="hidden">
<input name="HSP_RANGE_MAX" value="0" type="hidden">
<input name="JOB_TITLE" value="Protein Sequence (333 letters)" type="hidden">
<input name="LAYOUT" value="OneWindow" type="hidden">
<input name="LINE_LENGTH" value="60" type="hidden">
<input name="MASK_CHAR" value="2" type="hidden">
<input name="MASK_COLOR" value="1" type="hidden">
<input name="MATRIX_NAME" value="BLOSUM62" type="hidden">
<input name="MAX_NUM_SEQ" value="100" type="hidden">
<input name="MYNCBI_USER" value="9311188414" type="hidden">
<input name="NEW_VIEW" value="on" type="hidden">
<input name="NUM_DIFFS" value="0" type="hidden">
<input name="NUM_OPTS_DIFFS" value="0" type="hidden">
<input name="NUM_ORG" value="1" type="hidden">
<input name="NUM_OVERVIEW" value="100" type="hidden">
<input name="OLD_BLAST" value="false" type="hidden">
<input name="OLD_VIEW" value="false" type="hidden">
<input name="PAGE" value="Proteins" type="hidden">
<input name="PAGE_TYPE" value="BlastSearch" type="hidden">
<input name="PROGRAM" value="blastp" type="hidden">
<input name="QUERY_INDEX" value="0" type="hidden">
<input name="QUERY_INFO" value="Protein Sequence (333 letters)" type="hidden">
<input name="QUERY_LENGTH" value="333" type="hidden">
<input name="REPEATS" value="5755" type="hidden">
<input name="RID" value="NR8ZP8E1071" type="hidden">
<input name="RTOE" value="21" type="hidden">
<input name="SELECTED_PROG_TYPE" value="blastp" type="hidden">
<input name="SERVICE" value="plain" type="hidden">
<input name="SHORT_QUERY_ADJUST" value="on" type="hidden">
<input name="SHOW_LINKOUT" value="on" type="hidden">
<input name="SHOW_OVERVIEW" value="on" type="hidden">
<input name="USER_DEFAULT_MATRIX" value="4" type="hidden">
<input name="USER_DEFAULT_PROG_TYPE" value="blastp" type="hidden">
<input name="USER_TYPE" value="2" type="hidden">
<input name="WORD_SIZE" value="3" type="hidden">
<input name="db" value="protein" type="hidden">
<input name="stype" value="protein" type="hidden">
<input name="x" value="41" type="hidden">
<input name="y" value="12" type="hidden">
</form>

这也会为程序创建一个 POST 请求,最感兴趣的是字段RID,它将请求与您的初始查询参数链接起来。这可能存储在数据库或临时文件中,并分配了一个 ID,该 ID 在几小时后到期。

提交此表单时,会提供许多有趣的信息,并在创建它的表单的 POST 请求中呈现。上述字段之一可能指定要显示的初始对齐数。如果你随后重新打开 JavaScript,你会发现指向页面末尾(它本身已经是几个屏幕)将使用这个程序加载另一个块:

http://blast.ncbi.nlm.nih.gov/t2g.cgi?CMD=Get&RID=NR8ZP8E1071&OLD_BLAST=false&DESCRIPTIONS=0&NUM_OVERVIEW=0&GET_SEQUENCE=on&DYNAMIC_FORMAT=on&ALIGN_SEQ_LIST=gi|160797,gi|9816,gi|121273,gi|428230092,gi|417051&HSP_SORT=0&SEQ_LIST_START=1&QUERY_INDEX=0&SHOW_LINKOUT=on&ALIGNMENT_VIEW=Pairwise&MASK_CHAR=2&MASK_COLOR=1&LINE_LENGTH=60

有趣的是,这里使用了 GET 请求。使用 Firefox 中的网络监视器,我触发了一系列这些,以查看是否可以发现一系列递增的数字。我发现SEQ_LIST_START 从 1 开始并以 5 个块为单位递增,但我不确定ALIGN_SEQ_LIST 中的元素来自哪里——可能来自当前页面。值得你亲自看看,看看你是否能发现任何东西 - 特别是因为你会以我不了解的方式理解主题。

您也许可以修改此链接中的一些查询字符串参数,以查看是什么控制了返回的项目数。但是,请注意:如果您请求他们的系统习惯的更大的集合,您可能会被注意到并在您的 IP 地址上放置一个块。

此外,请记住,如果您抓取一个网站,您就是passing your costs onto a third party。由于数据似乎是免费提供的,这在某种程度上是他们可以接受的,也是他们已经花费的资金的好处。但是,请注意您在他们的服务器上施加的负载:不要请求过大的块,并在每个请求之间设置几秒钟的延迟。

如果您打算获取大量数据(比如说超过半 GB),那么在等待几秒钟到几分钟之间交替进行,或者也许在晚上(他们的时间)当他们的服务器时集中您的下载可能不那么忙。未能作为爬虫“负责任地行事”可能会将您的 IP 范围置于其阻止列表中,在最坏的情况下可能构成拒绝服务攻击。

所以,总而言之,您需要做以下事情:

  • 发出初始 POST 请求以检索表单
  • 等待几秒钟
  • 获取响应(尤其是请求 ID)并在新的 POST 中重新提交该数据
  • 从屏幕上收集数据
  • 在第二个程序中使用 GET 请求获取新数据
  • 从响应中收集新数据

愿意修改你的 POST 和 GET 参数来看看效果,玩得开心!

【讨论】:

  • 您一定花了很长时间才整理出如此完整的答案,赞! (和我 +1)
  • 感谢 Purefan。是的,但我不介意修修补补 - 这是在周六下午花 20 分钟的好方法!
  • 非常感谢halfer,这真的很有帮助。所以如果我理解得很好,我是否也应该从我的爬虫中禁用Javascript?或者直接忽略它
  • 不,你只需要坚持一点。尝试这样的事情(未经测试):$form = $crawler-&gt;filter('form[name=X]');。它真的非常灵活——它唯一的小毛病是文档有点稀疏。它在内部使用 Guzzle,它具有您可以附加到的各种事件。
  • 谢谢,成功了。 :-) 我会尽快接受答案,因为它真的很有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多