使用__doPostBack抓取隐藏链接网址
我正在尝试从一个使用 __doPostBack
函数的网站上抓取搜索结果。这个网页每次搜索会显示10个结果。如果想看到更多结果,就需要点击一个按钮,这个按钮会触发一个 __doPostBack
的JavaScript代码。经过一些研究,我发现这个POST请求的行为就像一个表单,因此可以简单地使用scrapy的 FormRequest
来填写这个表单。我参考了以下的讨论:
使用scrapy处理javascript的__doPostBack方法时遇到的问题
然后写了以下脚本。
# -*- coding: utf-8 -*-
from scrapy.contrib.spiders import CrawlSpider
from scrapy.http import FormRequest
from scrapy.http import Request
from scrapy.selector import Selector
from ahram.items import AhramItem
import re
class MySpider(CrawlSpider):
name = u"el_ahram2"
def start_requests(self):
search_term = u'اقتصاد'
baseUrl = u'http://digital.ahram.org.eg/sresult.aspx?srch=' + search_term + u'&archid=1'
requests = []
for i in range(1, 4):#crawl first 3 pages as a test
argument = u"'Page$"+ str(i+1) + u"'"
data = {'__EVENTTARGET': u"'GridView1'", '__EVENTARGUMENT': argument}
currentPage = FormRequest(baseUrl, formdata = data, callback = self.fetch_articles)
requests.append(currentPage)
return requests
def fetch_articles(self, response):
sel = Selector(response)
for ref in sel.xpath("//a[contains(@href,'checkpart.aspx?Serial=')]/@href").extract():
yield Request('http://digital.ahram.org.eg/' + ref, callback=self.parse_items)
def parse_items(self, response):
sel = Selector(response)
the_title = ' '.join(sel.xpath("//title/text()").extract()).replace('\n','').replace('\r','').replace('\t','')#* mean 'anything'
the_authors = '---'.join(sel.xpath("//*[contains(@id,'editorsdatalst_HyperLink')]//text()").extract())
the_text = ' '.join(sel.xpath("//span[@id='TextBox2']/text()").extract())
the_month_year = ' '.join(sel.xpath("string(//span[@id = 'Label1'])").extract())
the_day = ' '.join(sel.xpath("string(//span[@id = 'Label2'])").extract())
item = AhramItem()
item["Authors"] = the_authors
item["Title"] = the_title
item["MonthYear"] = the_month_year
item["Day"] = the_day
item['Text'] = the_text
return item
现在我遇到的问题是 'fetch_articles' 从来没有被调用:
2014-05-27 12:19:12+0200 [scrapy] DEBUG: Web service listening on 0.0.0.0:6080
2014-05-27 12:19:13+0200 [el_ahram2] DEBUG: Crawled (200) <POST http://digital.ahram.org.eg/sresult.aspx?srch=%D8%A7%D9%82%D8%AA%D8%B5%D8%A7%D8%AF&archid=1> (referer: None)
2014-05-27 12:19:13+0200 [el_ahram2] DEBUG: Crawled (200) <POST http://digital.ahram.org.eg/sresult.aspx?srch=%D8%A7%D9%82%D8%AA%D8%B5%D8%A7%D8%AF&archid=1> (referer: None)
2014-05-27 12:19:13+0200 [el_ahram2] DEBUG: Crawled (200) <POST http://digital.ahram.org.eg/sresult.aspx?srch=%D8%A7%D9%82%D8%AA%D8%B5%D8%A7%D8%AF&archid=1> (referer: None)
2014-05-27 12:19:13+0200 [el_ahram2] INFO: Closing spider (finished)
经过几天的搜索,我感觉完全卡住了。我是Python的新手,所以可能错误很简单。不过如果不是的话,这个讨论可能对很多人都有帮助。谢谢你们的帮助。
2 个回答
1
def fetch_articles(self, response):
sel = Selector(response)
print response._get_body() # you can write to file and do an grep
for ref in sel.xpath("//a[contains(@href,'checkpart.aspx?Serial=')]/@href").extract():
yield Request('http://digital.ahram.org.eg/' + ref, callback=self.parse_items)
我找不到你在找的“checkpart.aspx?Serial=”。
这可能无法解决你的问题,但建议用答案而不是评论来格式化代码。
4
你的代码没问题。fetch_articles
正在运行。你可以通过添加一个打印语句来测试它。
不过,网站要求你验证 POST 请求。为了验证这些请求,你必须在请求体中包含 __EVENTVALIDATION
和 __VIEWSTATE
,这样才能证明你是在回应他们的表单。要获取这些信息,你需要先发送一个 GET 请求,从表单中提取这些字段。如果你不提供这些信息,就会出现错误页面,而这个页面上没有包含 "checkpart.aspx?Serial=" 的链接,所以你的 for
循环没有被执行。
下面是我设置的 start_request
,然后 fetch_search
就是执行 start_request
原本的功能。
class MySpider(CrawlSpider):
name = u"el_ahram2"
def start_requests(self):
search_term = u'اقتصاد'
baseUrl = u'http://digital.ahram.org.eg/sresult.aspx?srch=' + search_term + u'&archid=1'
SearchPage = Request(baseUrl, callback = self.fetch_search)
return [SearchPage]
def fetch_search(self, response):
sel = Selector(response)
search_term = u'اقتصاد'
baseUrl = u'http://digital.ahram.org.eg/sresult.aspx?srch=' + search_term + u'&archid=1'
viewstate = sel.xpath("//input[@id='__VIEWSTATE']/@value").extract().pop()
eventvalidation = sel.xpath("//input[@id='__EVENTVALIDATION']/@value").extract().pop()
for i in range(1, 4):#crawl first 3 pages as a test
argument = u"'Page$"+ str(i+1) + u"'"
data = {'__EVENTTARGET': u"'GridView1'", '__EVENTARGUMENT': argument, '__VIEWSTATE': viewstate, '__EVENTVALIDATION': eventvalidation}
currentPage = FormRequest(baseUrl, formdata = data, callback = self.fetch_articles)
yield currentPage
...