使用多个重定向爬网页面的最佳方式


Best way to crawl a page with multiple redirects

我想抓取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一样。感谢


更新

我尝试了下面的一个建议——高特。

这是我的新代码:

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是我上面给出的蛋白质序列。好的部分是:它发布,给我新的页面,但没有遵循所有的重定向。如何使它遵循所有重定向?

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

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

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

请求ID:NR8ZP8E1071

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

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

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

我的猜测是,在重载时,这里的延迟(目前设置为1000ms,即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&描述=0&NUM_OVERVIEW=0&GET_SEQUENCE=打开&DYNAMIC_FORMAT=打开&ALIGN_SEQ_LIST=gi | 160797,gi | 9816,gi | 121273,gi | 428230092,gi | 417051&HSP_ SORT=0&SEQ_LIstrongTART=1&QUERY_INDEX=0&SHOW_LINKOUT=打开&ALIGNMENT_VIEW=成对&MASK_ CHAR=2&MASK_ COLOR=1&线路长度=60

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

您可以修改这个链接中的一些查询字符串参数,看看是什么控制了返回的项数。然而,要小心:如果你要求一个比他们的系统习惯的大得多的集合,你可能会被注意到,并在你的IP地址上放置了一个块。

除此之外,请记住,如果你抓取网站,你就是在把成本转嫁给第三方。由于这些数据似乎是免费的,这在一定程度上是他们可以接受的,这也是他们已经花费的资金的好处。然而,要注意你在他们的服务器上施加的负载:不要请求过大的块,并且在每个请求之间留出几秒钟的延迟。

如果你计划获取大量数据(比如超过半GB),那么每隔几秒到几分钟等待,或者在晚上(他们的时间)集中下载,因为他们的服务器可能不那么忙。作为爬虫,如果不"负责任地行事",可能会将您的IP范围列入他们的阻止列表,在最坏的情况下,可能构成拒绝服务攻击。

总之,以下是您需要做的:

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

愿意修改您的POST和GET参数以查看效果,并享受乐趣!