返回博客
指南
斯特凡·拉西拉2023年4月11日阅读时间:10分钟

如何使用 Python 制作网络爬虫 - 入门指南

如何使用 Python 制作网络爬虫 - 入门指南

先决条件

在阅读本文之前,您需要具备Python的基础知识,并且已在计算机上安装了Python。此外,您还需要安装Requests和BeautifulSoup模块。您可以在命令提示符或终端中运行以下命令来完成安装:

$ pip install requests bs4

在本文的第二部分中,我们将使用 Scrapy 构建一个示例网络爬虫,因此您需要安装 Scrapy 框架。该框架的开发者强烈建议您在专用的虚拟环境中安装 Scrapy,以避免与系统包发生冲突。

建议您安装virtualenvvirtualenvwrapper来创建一个隔离的 Python 环境。请注意,virtualenvwrapper 有一个适用于 Windows 的版本,名为virtualenvwrapper-win

此外,您还需要通过 pip 安装pipx,才能安装 virtualenv。

$ python -m pip install --user pipx

$ python -m pipx ensurepath

创建隔离的 Python 环境后,您可以使用以下命令安装 Scrapy。

$ pip install Scrapy

您可以在这里 找到 Scrapy 的安装指南

什么是网络爬虫?

网页爬取和 网页抓取是相关但不同的概念。网页抓取是指从网站中提取数据的整体过程。网页爬取则是自动浏览网页以查找需要抓取的URL的具体任务。

网络爬虫首先会获取一份待访问的URL列表,即所谓的“种子”。在遍历每个URL时,它会在HTML中搜索链接,并根据特定标准进行筛选。发现的任何新链接都会被添加到队列中,以便后续处理。随后,提取出的HTML或指定信息会被传递到另一个处理流程中进行进一步处理。

一个网络爬虫管道的流程图,展示了URL种子、队列、下载、处理、链接提取和数据存储等环节

在创建网络爬虫时,需要注意的是,并非网站上的所有页面都会被访问。实际访问的页面数量取决于爬虫的资源预算、爬取深度或执行所分配的时间。

许多网站都包含一个robots.txt文件,用于指定网站哪些部分可以被爬虫抓取,哪些部分应被避开。此外,部分网站还提供sitemap.xml文件,其指示比 robots.txt 更为明确,不仅会具体告知爬虫应抓取哪些页面,还会为每个 URL 提供额外的元数据。

网络爬虫通常用于多种用途:

  • SEO分析工具除了收集HTML数据外,还会收集响应时间和响应状态等元数据,以检测失效页面以及不同域名之间的链接,从而收集反向链接。
  • 价格监控工具会爬取电子商务网站,查找产品页面并提取元数据,特别是价格信息。随后,系统会定期重新访问这些产品页面。
  • 诸如 Googlebot、Bingbot 和 Yandex Bot 之类的搜索引擎会收集网络上大部分内容的 HTML 代码,并利用这些数据使其可供搜索。

在本文后面的内容中,我们将对比两种使用 Python 构建网络爬虫的方法。第一种方法是使用 Requests 库发送 HTTP 请求,并使用 BeautifulSoup 解析 HTML 内容。第二种方法则是使用网络爬虫框架,我们将使用 Scrapy。

使用 Requests 和 BeautifulSoup 库

Python 中的 requests 模块是用于发送 HTTP 请求的强大工具。若要将其用于网页爬取,你可以先导入该模块,然后向特定 URL 发送请求。例如:

url = 'https://amazon.com/s?k=baby+products'

response = requests.get(url)

获取响应后,您可以使用 BeautifulSoup 从 HTML 内容中提取所有链接。例如:

import json

from urllib.parse import urljoin

from bs4 import BeautifulSoup

html = response.text

links = []

soup = BeautifulSoup(html, 'html.parser')

for link in soup.find_all('a'):

    path = link.get('href')

    if path and path.startswith('/'):

        path = urljoin(url, path)

   

    links.append(path)

print(json.dumps(links, sort_keys = True, indent = 2))

然后,你可以遍历这些链接并向它们发送请求,重复这个过程,直到访问完所有想要爬取的页面。这是一个递归函数,其工作原理正是如此:

import requests

from urllib.parse import urljoin

from bs4 import BeautifulSoup

import logging

logging.basicConfig(

    format='%(asctime)s %(levelname)s:%(message)s',

    level=logging.INFO)

url = 'https://amazon.com/s?k=baby+products'

visited = []

def crawl(url):

    logging.info(f'Crawling: {url}')

    visited.append(url)

    html = ''

    try:

        html = requests.get(url).text

    except Exception:

        logging.exception(f'Failed to crawl: {url}')

        return

        

    soup = BeautifulSoup(html, 'html.parser')

    # here you can extract and store useful data from the page

    for link in soup.find_all('a'):

        path = link.get('href')

        if path and path.startswith('/'):

            path = urljoin(url, path)

        

        if path not in visited:

            crawl(path)

crawl(url)

该函数会为每个访问过的 URL 记录一行日志。

2023-01-16 09:20:51,681 信息:爬取中:https://amazon.com/s?k=baby+products

2023-01-16 09:20:53,053 信息:爬取中:https://amazon.com/ref=cs_503_logo

2023-01-16 09:20:54,195 信息:爬取:https://amazon.com/ref=cs_503_link

2023-01-16 09:20:55,131 信息:爬取:https://amazon.com/dogsofamazon/ref=cs_503_d

2023-01-16 09:20:56,549 信息:爬取:https://www.amazon.com/ref=nodl_?nodl_android

2023-01-16 09:20:57,071 信息:爬取:https://www.amazon.com/ref=cs_503_logo

2023-01-16 09:20:57,690 信息:爬取:https://www.amazon.com/ref=cs_503_link

2023-01-16 09:20:57,943 信息:爬取:https://www.amazon.com/dogsofamazon/ref=cs_503_d

2023-01-16 09:20:58,413 信息:爬取:https://www.amazon.com.au/ref=nodl_&nodl_android

2023-01-16 09:20:59,555 信息:爬取:无

2023-01-16 09:20:59,557 错误:抓取失败:无

虽然一个基础网页爬虫的代码看似简单,但要想成功爬取整个网站,必须克服许多挑战。这些挑战包括:

  • 下载 URL 的处理逻辑缺少重试机制,而且当 URL 数量较多时,URL 队列的效率并不高。
  • 该爬虫不会进行自我标识,且会忽略 robots.txt 文件。
  • 该爬虫运行缓慢,且不支持并行处理。爬取每个 URL 大约需要一秒钟,且爬虫会在等待响应后才继续处理下一个 URL。
  • 链接提取逻辑不支持通过移除查询字符串参数来规范化 URL,不处理相对锚点/片段 URL(例如 href="#anchor"),也不支持按域名过滤 URL 或过滤对静态文件的请求。

在下一节中,我们将了解 Scrapy 是如何解决这些问题的,以及它如何让扩展网络爬虫的功能以适应自定义用例变得轻而易举。

如何使用 Scrapy 框架用 Python 制作网络爬虫

Scrapy 是一个用于在 Python 中创建网络爬虫的强大框架。它提供了内置的方法来跟随链接并从网页中提取信息。您需要创建一个新的 Scrapy 项目和一个爬虫,以定义爬虫的行为。

在开始爬取亚马逊这样的网站之前,务必检查该网站的 robots.txt文件,以确认哪些 URL 路径是被允许的。当 ROBOTSTXT_OBEY 设置为 true 时,Scrapy 会自动读取该文件并遵循其规则;对于使用 Scrapy 命令 `startproject` 创建的项目,此设置为默认值。

要创建一个新的 Scrapy 项目,你需要运行以下命令:

$ scrapy startproject amazon_crawler

此命令将生成一个具有以下结构的项目:

amazon_crawler/

├── scrapy.cfg

└── amazon_crawler

    ├── __init__.py

    ├── items.py

    ├── middlewares.py

    ├── pipelines.py

    ├── settings.py

    └── spiders

        ├── __init__.py

要创建一个爬虫,请使用 Scrapy 命令行界面中的 `genspider` 命令。该命令的定义如下:

$ scrapy genspider [options] <name> <domain>

要为该爬虫生成一个蜘蛛,我们可以运行:

$ cd amazon_crawler

$ scrapy genspider baby_products amazon.com

它应在名为 `spiders` 的文件夹内创建一个名为 `baby_products.py` 的文件,并生成以下代码:

import scrapy

class BabyProductsSpider(scrapy.Spider):

    name = 'wikipedia'

    allowed_domains = ['en.wikipedia.com']

    start_urls = ['http://en.wikipedia.com/']

    def parse(self, response):

        pass

Scrapy 还提供了多种现成的爬虫类,例如CrawlSpider、XMLFeedSpider、CSVFeedSpider 和 SitemapSpider。CrawlSpider 类基于基础的 Spider 类构建,它包含一个额外的“rules”属性,用于定义如何在网站中进行导航。每条规则都会使用LinkExtractor来确定应从每个页面中提取哪些链接。

在我们的用例中,我们应让 Spider 类继承自 CrawlSpider。此外,我们还需要制定一条 LinkExtractor 规则,指示爬虫仅从亚马逊的分页中提取链接。请记住,我们的目标是收集亚马逊上所有婴儿产品的数据,因此我们并不希望追踪页面上找到的所有链接。

然后,我们需要在类中再创建两个方法:`parse_item` 和 `parse_product`。`parse_item` 将作为回调函数传递给我们的 LinkExtractor 规则,并在每次提取链接时被调用。`parse_product` 将解析每个产品…… ¯\_(ツ)_/¯

from scrapy.spiders import CrawlSpider, Rule

from scrapy.linkextractors import LinkExtractor

from bs4 import BeautifulSoup

class BabyProductsSpider(CrawlSpider):

    name = 'baby_products'

    allowed_domains = ['amazon.com']

    start_urls = ['https://amazon.com/s?k=baby+products']

    rules = (

        Rule(

            LinkExtractor(

                restrict_css='.s-pagination-strip'

            ),

            callback='parse_item',

            follow=True),

        )

    def parse_item(self, response):

        soup = BeautifulSoup(response.text, 'html.parser')

        products = soup.select('div[data-component-type="s-search-result"]')

        data = []

        for product in products:

            parsed_product = self.parse_product(product)

            if (parsed_product != 'error'):

                data.append(parsed_product)

        return {

            'url': response.url,

            'data': data

        }

    def parse_product(self, product):

        try:

            link = product.select_one('a.a-text-normal')

            price = product.select_one('span.a-price > span.a-offscreen').text

            return {

                'product_url': link['href'],

                'name': link.text,

                'price': price

            }

        except:

            return 'error'

要启动爬虫,您可以运行:

$ scrapy crawl baby_products

您将在控制台中看到大量日志(您可以通过 `--logfile [log_file_name]` 指定日志文件)。

我以亚马逊搜索为例,演示了使用 Python 创建网络爬虫的基础知识。不过,该爬虫发现的可追踪链接不多,且并未针对特定数据使用场景进行优化。如果您希望从亚马逊搜索中提取特定数据,可以考虑使用我们的亚马逊产品数据 API。我们为亚马逊搜索、产品页面和分类页面开发了自定义解析器,该 API 返回的 JSON 格式数据可直接用于您的应用程序。

为什么使用专业的数据抓取服务比使用爬虫更好

虽然网络爬虫是提取互联网数据的有用工具,但其配置过程往往既耗时又复杂。此外,网络爬取可能违反某些网站的服务条款,导致您的IP地址被封禁,甚至面临法律诉讼。

另一方面,专业的数据抓取服务利用先进的技术手段绕过反抓取措施,在不被察觉的情况下提取数据。它们还负责抓取基础设施的维护和扩展,让您能够专注于数据的分析和利用。此外,由于它们能够处理更复杂的数据提取场景并胜任大规模抓取任务,因此能提供更高水平的数据准确性和完整性。

摘要

总而言之,虽然网络爬虫是提取互联网数据的有用工具,但其配置过程往往既耗时又复杂。此外,网络爬虫可能违反某些网站的服务条款,导致您的IP被封禁,甚至面临法律诉讼。因此,对于更复杂、大规模的爬取任务,最好使用专业的爬取服务。

如果您正在寻找替代自行爬取的方案,不妨考虑使用WebScrapingAPI。WebScrapingAPI 是一项专业的网页抓取服务,可让您轻松从网站中提取数据,而无需自行开发和维护网页抓取工具。

它是一种快速、可靠、经济高效的解决方案,适用于各种规模的企业。

今天来试试吧!完全免费,我们提供14天试用期,无需绑定银行卡。

关于作者
Ștefan Răcilă,全栈开发者 @ WebScrapingAPI
斯特凡·拉西拉全栈开发工程师

Stefan Racila 是 WebScrapingAPI 的 DevOps 及全栈工程师,负责开发产品功能并维护确保平台稳定运行的基础设施。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网页数据。