如何用scrapy刮掉每个链接的所有内容?

stu*_*ent 9 python web-crawler scrapy web-scraping scrapy-spider

我是scrapy的新手我想从这个网站上提取每个广告的所有内容.所以我尝试了以下方法:

from scrapy.spiders import Spider
from craigslist_sample.items import CraigslistSampleItem

from scrapy.selector import Selector
class MySpider(Spider):
    name = "craig"
    allowed_domains = ["craigslist.org"]
    start_urls = ["http://sfbay.craigslist.org/search/npo"]

    def parse(self, response):
        links = response.selector.xpath(".//*[@id='sortable-results']//ul//li//p")
        for link in links:
            content = link.xpath(".//*[@id='titletextonly']").extract()
            title = link.xpath("a/@href").extract()
            print(title,content)
Run Code Online (Sandbox Code Playgroud)

项目:

# Define here the models for your scraped items

from scrapy.item import Item, Field

class CraigslistSampleItem(Item):
    title = Field()
    link = Field()
Run Code Online (Sandbox Code Playgroud)

但是,当我运行爬虫时,我什么都没得到:

$ scrapy crawl --nolog craig
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
Run Code Online (Sandbox Code Playgroud)

因此,我的问题是:我如何遍历每个网址,进入每个链接并抓取内容和标题?,这是最好的方法吗?

Iva*_*aer 13

如果您想要抓取,可能需要查看CrawlSpider.

要构建一个基本的scrapy项目,您可以使用以下命令:

scrapy startproject craig
Run Code Online (Sandbox Code Playgroud)

然后添加蜘蛛和物品:

克雷格/蜘蛛/ spider.py

from scrapy.spiders import CrawlSpider, Rule
from craig.items import CraigslistSampleItem
from scrapy.linkextractors.lxmlhtml import LxmlLinkExtractor
from scrapy.selector import Selector

class MySpider(CrawlSpider):
    name = "craig"
    allowed_domains = ["craigslist.org"]
    start_urls = ["http://sfbay.craigslist.org/search/npo"]

    rules = (

        Rule(LxmlLinkExtractor(
            restrict_xpaths=(".//*[@id='sortable-results']//li//a")),
            follow=False,
            callback='parse_item'
        ),

      )

    def parse_item(self, response):

        sel = Selector(response)

        item = CraigslistSampleItem()

        item['title'] = sel.xpath('//*[@id="titletextonly"]').extract_first()
        item['body'] = sel.xpath('//*[@id="postingbody"]').extract_first()
        item['link'] = response.url

        yield item
Run Code Online (Sandbox Code Playgroud)

克雷格/ items.py

# -*- coding: utf-8 -*-

# Define here the models for your scraped items

from scrapy.item import Item, Field

class CraigslistSampleItem(Item):
    title = Field()
    body = Field()
    link = Field()
Run Code Online (Sandbox Code Playgroud)

克雷格/ settings.py

# -*- coding: utf-8 -*-

BOT_NAME = 'craig'

SPIDER_MODULES = ['craig.spiders']
NEWSPIDER_MODULE = 'craig.spiders'

ITEM_PIPELINES = {
   'craig.pipelines.CraigPipeline': 300,
}
Run Code Online (Sandbox Code Playgroud)

克雷格/ pipelines.py

from scrapy import signals
from scrapy.xlib.pydispatch import dispatcher
from scrapy.exporters import CsvItemExporter

class CraigPipeline(object):

    def __init__(self):
        dispatcher.connect(self.spider_opened, signals.spider_opened)
        dispatcher.connect(self.spider_closed, signals.spider_closed)
        self.files = {}

    def spider_opened(self, spider):
        file = open('%s_ads.csv' % spider.name, 'w+b')
        self.files[spider] = file
        self.exporter = CsvItemExporter(file)
        self.exporter.start_exporting()

    def spider_closed(self, spider):
        self.exporter.finish_exporting()
        file = self.files.pop(spider)
        file.close()

    def process_item(self, item, spider):
        self.exporter.export_item(item)
        return item
Run Code Online (Sandbox Code Playgroud)

您可以通过运行命令来运行spider :

scrapy runspider scraper/spiders/spider.py
Run Code Online (Sandbox Code Playgroud)

从项目的根目录.

它应该craig_ads.csv在项目的根目录中创建一个.


roj*_*eer 5

我正在尝试回答您的问题。

首先,由于您的XPath 查询不正确,您得到了空白结果。通过 XPath ".//*[@id='sortable-results']//ul//li//p",您<p>正确定位了相关节点,但我不喜欢您的查询表达式。但是,我不知道您的以下 XPath 表达式".//*[@id='titletextonly']""a/@href",他们无法按您的预期找到链接和标题。也许你的意思是定位标题的文本和标题的超链接。如果是的话,我相信你必须学习 Xpath,并且请从HTML DOM开始。

我确实想指导您如何进行 XPath 查询,因为网上有很多资源。我想提一下 Scrapy XPath 选择器的一些特性:

  1. Scrapy XPath Selector是标准 XPath 查询的改进包装器。

在标准 XPath 查询中,它返回您查询的 DOM 节点数组。您可以打开浏览器(F12)的开发模式,使用控制台命令$x(x_exp)进行测试。我强烈建议通过这种方式测试您的 XPath 表达式。它将为您提供即时结果并节省大量时间。如果有时间,熟悉一下浏览器的web开发工具,可以让你快速了解网页结构,找到你要找的入口。

而 Scrapyresponse.xpath(x_exp)返回一个Selector对应于实际 XPath 查询的对象数组,它实际上是一个SelectorList对象。这意味着 XPath 结果由SelectorsList. 并且classSelectorSelectorListclass 都提供了一些有用的函数来操作结果:

  • extract, 返回序列化文档节点列表(以 unicode 字符串)
  • extract_first,返回标,first对的extract结果
  • re,返回一个列表,re该的extract结果
  • re_first,返回标,first对的re结果。

这些功能使您的编程更加方便。一个例子是你可以xpath直接在SelectorList对象上调用函数。如果您lxml之前尝试过,您会发现这非常有用:如果您想对xpath前一个xpath结果的结果调用函数,则lxml必须迭代前一个结果。另一个例子是,当您确定该列表中最多只有一个元素时,您可以使用extract_first获取一个标量值,而不是使用列表索引方法(例如,rlist[0]),当没有元素匹配时,它会导致索引外异常。请记住,解析网页时总会有例外,请谨慎并健壮地编程。

  1. 绝对 XPath 与相对 XPath

请记住,如果您正在嵌套 XPathSelector 并使用以 / 开头的 XPath,则该 XPath 将是文档的绝对路径,而不是相对于您从中调用它的 XPathSelector。

操作时node.xpath(x_expr),如果x_expr以 开头/,则为绝对查询,XPath 会从开始搜索root;else 如果x_expr以 开头.,则是相对查询。这也在标准2.5 Abbreviated Syntax 中注明

. 选择上下文节点

.//para 选择上下文节点的para元素后代

.. 选择上下文节点的父节点

../@lang 选择上下文节点的父级的lang属性

  1. 如何关注下一页和关注结束。

对于您的应用程序,您可能需要遵循下一页。在这里,下一个页面节点很容易找到——有下一个按钮。但是,您还需要注意停止关注的时间。仔细查看您的 URL 查询参数以了解应用程序的 URL 模式。在这里,要确定何时停止关注下一页,您可以将当前项目范围与项目总数进行比较。

新编辑

对链接内容的含义有点困惑。现在我知道@student 也想抓取链接以提取 AD 内容。下面是一个解决方案。

  1. 发送请求并附加其解析器

您可能会注意到,我使用 ScrapyRequest类来跟随下一页。实际上,Request类的功能不止于此——您可以通过设置参数为每个请求附加所需的解析函数callback

callback (callable) – 将使用此请求的响应(一旦下载)作为其第一个参数调用的函数。有关更多信息,请参阅下面的将附加数据传递给回调函数。如果请求未指定回调,则将使用蜘蛛的 parse() 方法。请注意,如果在处理期间引发异常,则会调用 errback。

在步骤 3 中,callback发送下一页请求时我没有设置,因为这些请求应该由默认parse函数处理。现在来到指定的 AD 页面,与之前的 AD 列表页面不同的页面。因此我们需要定义一个新的页面解析器函数,比方说parse_ad,当我们发送每个 AD 页面请求时,将这个parse_ad函数附加到请求中。

让我们转到对我有用的修改后的示例代码:

项目.py

# -*- coding: utf-8 -*-

# Define here the models for your scraped items
#
# See documentation in:
# http://doc.scrapy.org/en/latest/topics/items.html

import scrapy


class ScrapydemoItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title = scrapy.Field()
    link = scrapy.Field()


class AdItem(scrapy.Item):
    title = scrapy.Field()
    description = scrapy.Field()
Run Code Online (Sandbox Code Playgroud)

蜘蛛

# -*- coding: utf-8 -*-
from scrapy.spiders import Spider
from scrapy.http import Request
from scrapydemo.items import ScrapydemoItem
from scrapydemo.items import AdItem
try:
    from urllib.parse import urljoin
except ImportError:
    from urlparse import urljoin


class MySpider(Spider):
    name = "demo"
    allowed_domains = ["craigslist.org"]
    start_urls = ["http://sfbay.craigslist.org/search/npo"]

    def parse(self, response):
        # locate list of each item
        s_links = response.xpath("//*[@id='sortable-results']/ul/li")
        # locate next page and extract it
        next_page = response.xpath(
            '//a[@title="next page"]/@href').extract_first()
        next_page = urljoin(response.url, next_page)
        to = response.xpath(
            '//span[@class="rangeTo"]/text()').extract_first()
        total = response.xpath(
            '//span[@class="totalcount"]/text()').extract_first()
        # test end of following
        if int(to) < int(total):
            # important, send request of next page
            # default parsing function is 'parse'
            yield Request(next_page)

        for s_link in s_links:
            # locate and extract
            title = s_link.xpath("./p/a/text()").extract_first().strip()
            link = s_link.xpath("./p/a/@href").extract_first()
            link = urljoin(response.url, link)
            if title is None or link is None:
                print('Warning: no title or link found: %s', response.url)
            else:
                yield ScrapydemoItem(title=title, link=link)
                # important, send request of ad page
                # parsing function is 'parse_ad'
                yield Request(link, callback=self.parse_ad)

    def parse_ad(self, response):
        ad_title = response.xpath(
            '//span[@id="titletextonly"]/text()').extract_first().strip()
        ad_description = ''.join(response.xpath(
            '//section[@id="postingbody"]//text()').extract())
        if ad_title is not None and ad_description is not None:
            yield AdItem(title=ad_title, description=ad_description)
        else:
            print('Waring: no title or description found %s', response.url)
Run Code Online (Sandbox Code Playgroud)

重点说明

输出快照:

2016-11-10 21:25:14 [scrapy] DEBUG: Scraped from <200 http://sfbay.craigslist.org/eby/npo/5869108363.html>
{'description': '\n'
                '        \n'
                '            QR Code Link to This Post\n'
                '            \n'
                '        \n'
                'Agency History:\n' ........
 'title': 'Staff Accountant'}
2016-11-10 21:25:14 [scrapy] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 39259,
 'downloader/request_count': 117,
 'downloader/request_method_count/GET': 117,
 'downloader/response_bytes': 711320,
 'downloader/response_count': 117,
 'downloader/response_status_count/200': 117,
 'finish_reason': 'shutdown',
 'finish_time': datetime.datetime(2016, 11, 11, 2, 25, 14, 878628),
 'item_scraped_count': 314,
 'log_count/DEBUG': 432,
 'log_count/INFO': 8,
 'request_depth_max': 2,
 'response_received_count': 117,
 'scheduler/dequeued': 116,
 'scheduler/dequeued/memory': 116,
 'scheduler/enqueued': 203,
 'scheduler/enqueued/memory': 203,
 'start_time': datetime.datetime(2016, 11, 11, 2, 24, 59, 242456)}
2016-11-10 21:25:14 [scrapy] INFO: Spider closed (shutdown)
Run Code Online (Sandbox Code Playgroud)

谢谢。希望这会有所帮助并玩得开心。