返回博客
指南
Ștefan RăcilăLast updated on Mar 31, 20262 min read

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

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

网络爬虫是指自动访问网页并从中提取有用信息的过程。网络爬虫(也称为蜘蛛或机器人)是一种执行此任务的程序。在本文中,我们将探讨如何使用 Python 编程语言创建网络爬虫。具体来说,我们将制作两个网络爬虫。

我们将利用 RequestsBeautifulSoup 库,从零开始用 Python 构建一个简单的网络爬虫。随后,我们将探讨使用 Scrapy 这类网络爬虫框架的优势。最后,我们将使用 Scrapy 构建一个示例爬虫,用于从亚马逊收集所有婴儿产品的数据。同时,我们还将了解 Scrapy 如何处理拥有数百万页面的网站。

先决条件

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

$ pip install requests bs4

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

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

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

$ python -m pip install --user pipx

$ python -m pipx ensurepath

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

$ pip install Scrapy

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

什么是网络爬虫?

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

网络爬虫通常从一组待访问的 URL 列表(即“种子”)开始。在遍历每个 URL 时,它会搜索 HTML 中的链接,并根据特定标准进行筛选。发现的任何新链接都会被添加到队列中以备后续处理。随后,提取的 HTML 或指定信息会被传递到另一个处理管道中进行进一步处理。

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

许多网站都包含一个 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 INFO:Crawling: https://amazon.com/s?k=baby+products

2023-01-16 09:20:53,053 INFO:Crawling: https://amazon.com/ref=cs_503_logo

2023-01-16 09:20:54,195 INFO:Crawling: https://amazon.com/ref=cs_503_link

2023-01-16 09:20:55,131 INFO:Crawling: https://amazon.com/dogsofamazon/ref=cs_503_d

2023-01-16 09:20:56,549 INFO:Crawling: https://www.amazon.com/ref=nodl_?nodl_android

2023-01-16 09:20:57,071 INFO:Crawling: https://www.amazon.com/ref=cs_503_logo

2023-01-16 09:20:57,690 INFO:Crawling: https://www.amazon.com/ref=cs_503_link

2023-01-16 09:20:57,943 INFO:Crawling: https://www.amazon.com/dogsofamazon/ref=cs_503_d

2023-01-16 09:20:58,413 INFO:Crawling: https://www.amazon.com.au/ref=nodl_&nodl_android

2023-01-16 09:20:59,555 INFO:Crawling: None

2023-01-16 09:20:59,557 ERROR:Failed to crawl: None

虽然基础网页爬虫的代码看似简单,但要成功爬取整个网站,必须克服许多挑战。其中包括以下问题:

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

在下一节中,我们将了解 Scrapy 如何解决这些问题,并如何轻松扩展网络爬虫的功能以满足自定义用例的需求。

如何使用 Scrapy 框架在 Python 中构建网络爬虫

Scrapy 是一个用于在 Python 中创建网络爬虫的强大框架。它提供了内置的链接跟踪和网页信息提取功能。您需要创建一个新的 Scrapy 项目和一个蜘蛛(spider),以定义爬虫的行为。

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

要创建一个新的 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 CLI 中的 `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。我们为亚马逊搜索、商品和分类页面创建了自定义解析器,它返回的 JSON 格式数据可直接用于您的应用程序。

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

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

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

总结

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

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

这是一款快速、可靠且经济高效的解决方案,适合各种规模的企业。

何不今天试一试!该服务完全免费,并提供 14 天试用期,无需绑定信用卡。

关于作者
Ștefan Răcilă, 全栈开发工程师 @ WebScrapingAPI
Ștefan Răcilă全栈开发工程师

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

开始构建

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

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