使用 Scrapy 抓取 Python 数据
Posted
技术标签:
【中文标题】使用 Scrapy 抓取 Python 数据【英文标题】:Python data scraping with Scrapy 【发布时间】:2013-05-23 00:57:42 【问题描述】:我想从具有文本字段、按钮等的网站上抓取数据。我的要求是填写文本字段并提交表单以获取结果,然后从结果页面抓取数据点。
我想知道 Scrapy 是否有这个功能,或者是否有人可以推荐 Python 中的库来完成这个任务?
(已编辑) 我想从以下网站抓取数据:http://a836-acris.nyc.gov/DS/DocumentSearch/DocumentType
我的要求是从 ComboBoxes 中选择值并点击搜索按钮并从结果页面中抓取数据点。
附:我正在使用 selenium Firefox 驱动程序从其他网站抓取数据,但该解决方案并不好,因为 selenium Firefox 驱动程序依赖于 FireFox 的 EXE,即必须在运行刮板之前安装 Firefox。 Selenium Firefox 驱动程序为一个实例消耗大约 100MB 内存,我的要求是一次运行很多实例以加快抓取过程,因此也存在内存限制。 火狐在执行爬虫时有时会崩溃,不知道为什么。此外,我需要减少窗口的抓取,这在 Selenium Firefox 驱动程序的情况下是不可能的。 我的最终目标是在 Heroku 上运行爬虫,我在那里有 Linux 环境,所以 selenium Firefox 驱动程序无法在 Heroku 上运行。 谢谢
【问题讨论】:
【参考方案1】:基本上,您有很多工具可供选择:
scrapy beautifulsoup lxml mechanize requests(和grequests) selenium ghost.py这些工具有不同的用途,但可以根据任务混合在一起。
Scrapy 是一款功能强大且非常智能的工具,用于抓取网站、提取数据。但是,当涉及到操作页面时:点击按钮、填写表格 - 就变得更加复杂了:
有时,通过直接在 scrapy 中进行底层表单操作,可以轻松模拟填写/提交表单 有时,您必须使用其他工具来帮助进行抓取,例如机械化或硒如果您的问题更具体,这将有助于了解您应该使用或选择哪种工具。
看一个有趣的scrapy&selenium mix的例子。在这里,selenium 任务是点击按钮并为 scrapy 项目提供数据:
import time
from scrapy.item import Item, Field
from selenium import webdriver
from scrapy.spider import BaseSpider
class ElyseAvenueItem(Item):
name = Field()
class ElyseAvenueSpider(BaseSpider):
name = "elyse"
allowed_domains = ["ehealthinsurance.com"]
start_urls = [
'http://www.ehealthinsurance.com/individual-family-health-insurance?action=changeCensus&census.zipCode=48341&census.primary.gender=MALE&census.requestEffectiveDate=06/01/2013&census.primary.month=12&census.primary.day=01&census.primary.year=1971']
def __init__(self):
self.driver = webdriver.Firefox()
def parse(self, response):
self.driver.get(response.url)
el = self.driver.find_element_by_xpath("//input[contains(@class,'btn go-btn')]")
if el:
el.click()
time.sleep(10)
plans = self.driver.find_elements_by_class_name("plan-info")
for plan in plans:
item = ElyseAvenueItem()
item['name'] = plan.find_element_by_class_name('primary').text
yield item
self.driver.close()
更新:
这是一个关于如何在您的案例中使用 scrapy 的示例:
from scrapy.http import FormRequest
from scrapy.item import Item, Field
from scrapy.selector import htmlXPathSelector
from scrapy.spider import BaseSpider
class AcrisItem(Item):
borough = Field()
block = Field()
doc_type_name = Field()
class AcrisSpider(BaseSpider):
name = "acris"
allowed_domains = ["a836-acris.nyc.gov"]
start_urls = ['http://a836-acris.nyc.gov/DS/DocumentSearch/DocumentType']
def parse(self, response):
hxs = HtmlXPathSelector(response)
document_classes = hxs.select('//select[@name="combox_doc_doctype"]/option')
form_token = hxs.select('//input[@name="__RequestVerificationToken"]/@value').extract()[0]
for document_class in document_classes:
if document_class:
doc_type = document_class.select('.//@value').extract()[0]
doc_type_name = document_class.select('.//text()').extract()[0]
formdata = '__RequestVerificationToken': form_token,
'hid_selectdate': '7',
'hid_doctype': doc_type,
'hid_doctype_name': doc_type_name,
'hid_max_rows': '10',
'hid_ISIntranet': 'N',
'hid_SearchType': 'DOCTYPE',
'hid_page': '1',
'hid_borough': '0',
'hid_borough_name': 'ALL BOROUGHS',
'hid_ReqID': '',
'hid_sort': '',
'hid_datefromm': '',
'hid_datefromd': '',
'hid_datefromy': '',
'hid_datetom': '',
'hid_datetod': '',
'hid_datetoy': '',
yield FormRequest(url="http://a836-acris.nyc.gov/DS/DocumentSearch/DocumentTypeResult",
method="POST",
formdata=formdata,
callback=self.parse_page,
meta='doc_type_name': doc_type_name)
def parse_page(self, response):
hxs = HtmlXPathSelector(response)
rows = hxs.select('//form[@name="DATA"]/table/tbody/tr[2]/td/table/tr')
for row in rows:
item = AcrisItem()
borough = row.select('.//td[2]/div/font/text()').extract()
block = row.select('.//td[3]/div/font/text()').extract()
if borough and block:
item['borough'] = borough[0]
item['block'] = block[0]
item['doc_type_name'] = response.meta['doc_type_name']
yield item
将其保存在spider.py
并通过scrapy runspider spider.py -o output.json
和output.json
运行,您将看到:
"doc_type_name": "CONDEMNATION PROCEEDINGS ", "borough": "Borough", "block": "Block"
"doc_type_name": "CERTIFICATE OF REDUCTION ", "borough": "Borough", "block": "Block"
"doc_type_name": "COLLATERAL MORTGAGE ", "borough": "Borough", "block": "Block"
"doc_type_name": "CERTIFIED COPY OF WILL ", "borough": "Borough", "block": "Block"
"doc_type_name": "CONFIRMATORY DEED ", "borough": "Borough", "block": "Block"
"doc_type_name": "CERT NONATTCHMENT FED TAX LIEN ", "borough": "Borough", "block": "Block"
...
希望对您有所帮助。
【讨论】:
Selenium Firefox 驱动程序为一个实例消耗大约 100MB 内存,我的要求是一次运行很多实例以加快抓取过程,因此也存在内存限制。火狐在执行爬虫时有时会崩溃,不知道为什么。此外,我需要减少窗口的抓取,这在 Selenium Firefox 驱动程序的情况下是不可能的。我的最终目标是在 Heroku 上运行爬虫程序,我在那里有 Linux 环境,所以 selenium Firefox 驱动程序无法在 Heroku 上运行。 好的,看起来像一个简单的 html 表单,它发送一个 post 请求。只使用scrapy就足够了。理论上应该是这样的:抓取主页,从选择字段中获取选项,以回调开始Requests - 将数据抓取到回调中的scrapy项目中。如果你愿意,我可以举个例子。 是的,如果你能给出一个实现的小例子,这对我真的很有帮助。 我添加了一个示例。逐一读取Document Type
值,生成FormRequests
并从搜索结果中解析borough
和block
字段。如果有帮助,请考虑接受答案。快乐刮!
非常感谢! :) 你提供的例子对我很有帮助。【参考方案2】:
如果您只是想提交表单并从结果页面中提取数据,我会选择:
requests发送post请求 beautiful soup 从结果页面中提取所选数据Scrapy 的附加价值确实在于它能够跟踪链接和抓取网站,如果您确切地知道要搜索的内容,我认为它不是适合这项工作的工具。
【讨论】:
这只是我的意见,但我非常喜欢 lxml 而不是 B.S.明显更快,并且它有一个回退到 BS 的解析器。如果您尝试在格式错误的 html 上使用 lxml,只需捕获异常并将其提供给 lxml 内置的汤解析器。【参考方案3】:我个人会使用mechanize,因为我对scrapy 没有任何经验。但是,为屏幕抓取而构建的名为 scrapy 的库应该可以完成任务。我只想和他们两个一起去看看哪个做得最好/最简单。
【讨论】:
【参考方案4】:我使用过 Scrapy、Selenium 和 BeautifulSoup。 Scrapy 将帮助您完成工作。对我来说,BeautifulSoup 更有用,因为我可以使用 prettify()、find_all() 等函数,这对我理解 html 内容有很大帮助。
我不推荐 Selenium,因为它会减慢您的进程。它首先加载浏览器,它的内容,然后继续进行抓取,这导致它比其他包花费更长的时间。
【讨论】:
但另一方面,它将与最终的 DOM 一起工作,而不是与普通的源代码一起工作!以上是关于使用 Scrapy 抓取 Python 数据的主要内容,如果未能解决你的问题,请参考以下文章