Skip to content

Latest commit

 

History

History
324 lines (226 loc) · 11.3 KB

File metadata and controls

324 lines (226 loc) · 11.3 KB

第五章:使用 Scrapy 和 BeautifulSoup 进行网络抓取

在本章中,我们将涵盖以下内容:

  • 使用 Scrapy 的网络蜘蛛

  • Scrapy shell

  • 将提取器与 Scrapy 链接起来

  • 使用 Scrapy 登录网站后进行抓取

介绍

Scrapy是最强大的 Python 网络爬虫框架之一,它可以帮助高效地抓取网页的许多基本功能。

使用 Scrapy 的网络蜘蛛

网络蜘蛛从要访问的 URL 或 URL 列表开始,当蜘蛛获取新页面时,它会分析页面以识别所有超链接,并将这些链接添加到要爬行的 URL 列表中。只要发现新数据,这个动作就会递归地继续下去。

网络蜘蛛可以找到新的 URL 并对其进行索引以进行爬行,或者从中下载有用的数据。在下面的示例中,我们将使用 Scrapy 创建一个网络蜘蛛。

准备工作

我们可以从 Python 的pip命令安装 Scrapy:

pip install scrapy   

确保您有安装 Scrapy 所需的权限。如果权限出现任何错误,请使用sudo命令。

如何操作...

让我们用 Scrapy 创建一个简单的蜘蛛:

  1. 要创建一个新的蜘蛛项目,请打开终端并转到我们的蜘蛛所在的文件夹:
$ mkdir new-spider
$ cd new-spider  
  1. 然后运行以下命令创建一个带有scrapy的新蜘蛛项目:
$ scrapy startproject books  

这将创建一个名为books的项目,并创建一些有用的文件来创建爬虫。现在你有了一个文件夹结构,如下面的截图所示:

  1. 现在我们可以使用以下命令创建一个爬虫:
$ scrapy genspider home books.toscrape.com  

这将生成名为home的蜘蛛的代码,因为我们计划爬取books.toscrape.com的主页。现在spiders文件夹内的文件夹结构将如下所示:

  1. 如您所见,spiders文件夹内有一个名为home.py的文件。我们可以打开home.py并开始编辑它。home.py文件将包含以下代码:
# -*- coding: utf-8 -*- 
import scrapy 
class HomeSpider(scrapy.Spider): 
    name = 'home' 
    allowed_domains = ['books.toscrape.com'] 
    start_urls = ['http://books.toscrape.com/'] 
    def parse(self, response): 
        pass 

HomeSpiderscrapy.spider的子类。名称设置为home,这是我们在生成蜘蛛时提供的。allowed_domains属性定义了此爬虫的授权域,start_urls定义了爬虫要开始的 URL。

正如其名称所示,parse方法解析了所访问的 URL 的内容。

  1. 尝试使用以下命令运行蜘蛛:
$ scrapy crawl home    
  1. 现在我们可以重写蜘蛛以浏览分页链接:
from scrapy.spiders import CrawlSpider, Rule 
from scrapy.linkextractors import LinkExtractor  
class HomeSpider(CrawlSpider): 
    name = 'home' 
    allowed_domains = ['books.toscrape.com'] 
    start_urls = ['http://books.toscrape.com/'] 
    rules = (Rule(LinkExtractor(allow=(), restrict_css=('.next',)), 
             callback="parse_page", 
             follow=True),)  
    def parse_page(self, response): 
        print(response.url) 

要浏览多个页面,我们可以使用CrawlSpider的子类。从scrapy.spider导入CrawlSpiderRule模块。对于提取链接,我们可以使用scrapy.linkextractors中的LinkExtractor

然后我们需要设置rules变量,用于设置通过页面的规则。在这里,我们使用restrict_css参数来设置css类以到达下一页。可以通过在浏览器中检查网页来找到下一页 URL 的css类,如下面的截图所示:

  1. 通过以下命令运行爬虫来检查爬虫:
$ scrapy crawl home  

这将打印出蜘蛛解析的所有 URL。

  1. 让我们重写脚本以获取书籍的“标题”和“价格”。为此,我们必须为我们的项目创建一个类,因此在book项目内,我们将创建另一个名为item.py的文件,并定义我们要提取的项目:
from scrapy.item import Item, Field 
class BookItem(Item): 
    title = Field() 
    price = Field() 

在这里,我们定义了一个新类,其中包含我们希望从我们的蜘蛛中提取的细节。现在文件夹结构将如下所示:

  1. 然后,更新spider/home.py文件以提取数据:
from scrapy.spiders import CrawlSpider, Rule 
from scrapy.linkextractors import LinkExtractor 
from books.item import BookItem 
class HomeSpider(CrawlSpider): 
    name = 'home' 
    allowed_domains = ['books.toscrape.com'] 
    start_urls = ['http://books.toscrape.com/'] 
    rules = (Rule(LinkExtractor(allow=(), restrict_css=('.next',)), 
             callback="parse_page", 
             follow=True),) 
    def parse_page(self, response): 
        items = [] 
        books = response.xpath('//ol/li/article') 
        index = 0 
        for book in books: 
            item = BookItem() 
            title = books.xpath('//h3/a/text()')[index].extract() 
            item['title'] = str(title).encode('utf-8').strip() 
            price = books.xpath('//article/div[contains(@class, "product_price")]/p[1]/text()')[index].extract() 
            item['price'] = str(price).encode('utf-8').strip() 
            items.append(item) 
            index += 1 
            yield item 

更新parse_page方法以从每个页面提取“标题”和“价格”详情。要从页面中提取数据,我们必须使用选择器。在这里,我们使用了xpath选择器。XPath 是一种常用的语法或语言,用于浏览 XML 和 HTML 文档。

parse_page方法中,最初,我们选择了网站上放置书籍详细信息的所有文章标签,并遍历每个文章标签以解析书籍的标题和价格。

  1. 要获取标签的xpath选择器,我们可以使用谷歌 Chrome 浏览器的 XPath 工具,如下所示:

我们可以使用 Firefox Inspector 如下:

  1. 现在我们可以运行爬虫,将数据提取到.csv文件中:
$ scrapy crawl home -o book-data.csv -t csv   

这将在当前目录中创建一个名为book-data.csv的文件,其中包含提取的详细信息。

您可以在doc.scrapy.org/en/latest/topics/selectors.html了解有关选择器(如 XPath)以及如何从页面中选择详细信息的更多信息。

Scrapy shell

Scrapy shell 是一个命令行界面,可帮助调试脚本而无需运行整个爬虫。我们必须提供一个 URL,Scrapy shell 将打开一个接口,与爬虫在其回调中处理的对象进行交互,例如响应对象。

如何做...

我们可以通过一些简单的 Scrapy 交互式 shell 用法。步骤如下:

  1. 打开一个终端窗口,然后输入以下命令:
$ Scrapy shell http://books.toscrape.com/  

加载 Scrapy shell 后,它将打开一个接口,与响应对象进行交互,如下所示:

  1. 我们可以使用这个接口来调试response对象的选择器:
>>> response.xpath('//ol/li/article')  

这将打印选择器输出。有了这个,我们可以创建和测试爬虫的提取规则。

  1. 我们还可以从代码中打开 Scrapy shell 以调试提取规则中的错误。为此,我们可以使用inspect_response方法:
from scrapy.spiders import CrawlSpider, Rule 
from scrapy.linkextractors import LinkExtractor 
from scrapy.shell import inspect_response  
class HomeSpider(CrawlSpider): 
    name = 'home' 
    allowed_domains = ['books.toscrape.com'] 
    start_urls = ['http://books.toscrape.com/'] 
    rules = (Rule(LinkExtractor(allow=(), restrict_css=('.next',)), 
             callback="parse_page", 
             follow=True),)  
    def parse_page(self, response): 
        if len(response.xpath('//ol/li/article')) < 5: 
            title = response.xpath('//h3/a/text()')[0].extract() 
            print(title) 
        else: 
            inspect_response(response, self) 

如果条件失败,这将打开一个 shell 接口。在这里,我们已经导入了inspect_response并使用它来从代码中调试爬虫。

使用 Scrapy 的链接提取器

正如它们的名称所示,链接提取器是用于从 Scrapy 响应对象中提取链接的对象。Scrapy 具有内置的链接提取器,例如scrapy.linkextractors

如何做...

让我们用 Scrapy 构建一个简单的链接提取器:

  1. 与上一个示例一样,我们必须创建另一个 spider 来获取所有链接。

在新的spider文件中,导入所需的模块:

import scrapy 
from scrapy.linkextractor import LinkExtractor 
from scrapy.spiders import Rule, CrawlSpider  
  1. 创建一个新的spider类并初始化变量:
class HomeSpider2(CrawlSpider): 
    name = 'home2' 
    allowed_domains = ['books.toscrape.com'] 
    start_urls = ['http://books.toscrape.com/']
  1. 现在我们必须初始化爬取 URL 的规则:
rules = [ 
    Rule( 
        LinkExtractor( 
            canonicalize=True, 
            unique=True 
        ), 
        follow=True, 
        callback="parse_page" 
    ) 
]   

此规则命令提取所有唯一和规范化的链接,并指示程序跟随这些链接并使用parse_page方法解析它们

  1. 现在我们可以使用start_urls变量中列出的 URL 列表启动 spider:
def start_requests(self): 
    for url in self.start_urls: 
        yield scrapy.Request(url, callback=self.parse, dont_filter=True)  

start_requests()方法在打开爬虫进行爬取时调用一次

  1. 现在我们可以编写解析 URL 的方法:
def parse_page(self, response): 
    links = LinkExtractor(canonicalize=True, unique=True).extract_links(response) 
        for link in links: 
            is_allowed = False 
            for allowed_domain in self.allowed_domains: 
                if allowed_domain in link.url: 
                    is_allowed = True 
            if is_allowed: 
                print link.url 

该方法提取相对于当前响应的所有规范化和唯一的链接。它还验证链接的 URL 的域是否属于授权域中的一个。

使用 Scrapy 登录网站后进行爬取

有时我们必须登录网站才能访问我们计划提取的数据。使用 Scrapy,我们可以轻松处理登录表单和 cookies。我们可以利用 Scrapy 的FormRequest对象;它将处理登录表单并尝试使用提供的凭据登录。

准备工作

当我们访问一个需要身份验证的网站时,我们需要用户名和密码。在 Scrapy 中,我们需要相同的凭据来登录。因此,我们需要为我们计划抓取的网站获取一个帐户。

如何做...

以下是我们如何使用 Scrapy 来爬取需要登录的网站:

  1. 要使用FormRequest对象,我们可以按如下方式更新parse_page方法:
def parse(self, response): 
    return scrapy.FormRequest.from_response( 
        response, 
        formdata={'username': 'username', 'password': 'password'}, 
        callback=self.parse_after_login 
     ) 

在这里,响应对象是我们需要填写登录表单的页面的 HTTP 响应。FormRequest方法包括我们需要登录的凭据以及用于登录后解析页面的callback方法。

  1. 在保持登录会话的情况下进行分页,我们可以使用前面一篇食谱中使用的方法。