使用 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.jsonoutput.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 并从搜索结果中解析boroughblock 字段。如果有帮助,请考虑接受答案。快乐刮! 非常感谢! :) 你提供的例子对我很有帮助。【参考方案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 数据的主要内容,如果未能解决你的问题,请参考以下文章

爬虫框架:scrapy

scrapy爬虫框架介绍

使用 Scrapy 抓取 Python 数据

scrapy的使用

Python爬虫编程思想(152):使用Scrapy抓取数据,使用ItemLoader保存多条抓取的数据

Python爬虫编程思想(152):使用Scrapy抓取数据,使用ItemLoader保存多条抓取的数据