如何用scrapy清除每个链接的所有内容?

时间:2016-11-08 05:43:45

标签: python web-scraping scrapy web-crawler scrapy-spider

我是scrapy的新手我想从这个website中提取每个广告的所有内容。所以我尝试了以下内容:

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)

项目:

# Define here the models for your scraped items

from scrapy.item import Item, Field

class CraigslistSampleItem(Item):
    title = Field()
    link = Field()

然而,当我运行爬虫时,我什么都没得到:

$ scrapy crawl --nolog craig
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]

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

2 个答案:

答案 0 :(得分:13)

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

要构建一个基本的scrapy项目,您可以使用command

scrapy startproject craig

然后添加蜘蛛和物品:

  

克雷格/蜘蛛/ 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
  

克雷格/ 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()
  

克雷格/ settings.py

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

BOT_NAME = 'craig'

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

ITEM_PIPELINES = {
   'craig.pipelines.CraigPipeline': 300,
}
  

克雷格/ 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

您可以通过运行command

来运行蜘蛛
scrapy runspider scraper/spiders/spider.py

从项目的根目录开始。

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

答案 1 :(得分:4)

我想回答你的问题。

首先,由于您的不正确的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查询的改进包装器。
  2. 在标准XPath查询中,它返回您查询的DOM节点数组。您可以打开浏览器的开发模式(F12),使用控制台命令$x(x_exp)进行测试。我强烈建议通过这种方式测试你的XPath表达式。它将为您提供即时结果并节省大量时间。如果您有时间,请熟悉浏览器的Web开发工具,这样您就可以快速了解网页结构并找到您要查找的条目。

    而Scrapy response.xpath(x_exp)返回与实际XPath查询对应的Selector个对象数组,实际上是一个SelectorList对象。这意味着XPath结果由SelectorsList重新定义。 SelectorSelectorList类都提供了一些有用的函数来操作结果:

    • 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与relative XPath
    2.   

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

      当您执行操作node.xpath(x_expr)时,如果x_expr/开头,则为绝对查询,XPath将从root进行搜索;如果x_expr.开头,则为相对查询。标准2.5 Abbreviated Syntax

      中也提到了这一点
        

      。选择上下文节点

           

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

           

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

           

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

      1. 如何关注下一页并结束以下内容。
      2. 对于您的应用程序,您可能需要关注下一页。在这里,下一页节点很容易找到 - 有下一个按钮。但是,您还需要注意停止跟踪的时间。仔细查看您的URL查询参数,以告知您的应用程序的URL模式。在这里,要确定何时停止关注下一页,您可以将当前项目范围与项目总数进行比较。

        新编辑

        我对链接内容的含义感到有些困惑。现在我知道@student想要抓取链接以提取AD内容。以下是解决方案。

        1. 发送请求并附加其解析器
        2. 您可能会注意到我使用Scrapy Request类来关注下一页。实际上,Request类的强大功能超出了这一范围 - 您可以通过设置参数callback为每个请求附加所需的解析函数。

            

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

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

          让我们转到适用于我的修订示例代码:

          items.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()
          

          蜘蛛

          # -*- 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)
          

          关键提示

          输出快照:

          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)
          

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

相关问题