使用 Scrapy 进行网络抓取简单方法

米哈伊-马克西姆(Mihai Maxim),2023 年 1 月 30 日

博客图片

使用 Scrapy 进行网络搜刮

Scrapy 是一个强大的 Python 库,用于从网站中提取数据。它快速、高效、易用--相信我,我就是这样的人。无论你是数据科学家、开发人员,还是喜欢玩数据的人,Scrapy 都能为你提供帮助。最重要的是,它是免费开源的。

Scrapy 项目自带文件结构,可以帮助你组织代码和数据。它能让你更轻松地构建和维护网络搜刮工具,所以如果你打算认真进行网络搜刮,它绝对值得考虑。使用 Scrapy 进行网络搜刮 就像在你开始数据提取之旅的时候,身边多了一个得力助手(尽管是虚拟的)。

我们要建设的

当你学习一项新技能时,阅读是一回事,实际操作又是另一回事。这就是为什么我们决定在学习本指南时一起制作一个刮板。这是亲手了解如何使用 Scrapy 进行网页刮擦的最佳方式。

那么,我们到底要构建什么呢?我们将制作一个从 Urban Dictionary 网站上搜刮单词定义的搜刮器。这是一个有趣的搜刮目标,有助于让学习过程更加愉快。我们的搜刮器将很简单--它将返回在 Urban Dictionary 网站上找到的各种单词的定义。我们将使用 Scrapy 内置的从 HTML 文档中选择和提取数据的支持来提取我们需要的定义。

那么,让我们开始吧!在下一节中,我们将介绍学习本教程所需的先决条件。到时见!

先决条件

在我们开始构建刮板之前,有一些事情需要设置。在本节中,我们将介绍如何为我们的项目安装 Scrapy 和设置虚拟环境。Scrapy 文档建议将 Scrapy 安装在专用的虚拟环境中。这样做可以避免与系统软件包发生冲突。

我在 Ubuntu 22.04.1 WSL(Windows Subsystem for Linux)上运行 Scrapy,因此我将为我的机器配置一个虚拟环境。

我建议你阅读 "了解文件夹结构 "一章,以充分了解我们正在使用的工具。此外,请阅读 "Scrapy Shell "一章,它将使你的开发体验更加轻松。

设置 Python 虚拟环境

要在 Ubuntu 中为 Python 设置虚拟环境,可以使用 mkvirtualenv 命令。首先,确保已安装 virtualenv 和 virtualenvwrapper:

$ sudo apt-get install virtualenv virtualenvwrapper

在 .bashrc 文件末尾添加这几行:

export WORKON_HOME=$HOME/.virtualenvs

export PROJECT_HOME=$HOME/Deve

export VIRTUALENVWRAPPER_PYTHON='/usr/bin/python3'

源文件 /usr/local/bin/virtualenvwrapper.sh

我使用 Vim 编辑文件,但你也可以选择任何你喜欢的编辑器:

vim ~/.bashrc

// 使用 ctr + i 进入插入模式,使用向下箭头滚动到文件底部。

// 将这些行粘贴到文件末尾。

// 点击 escape 退出插入模式,输入 wq 并点击 enter 保存更改并退出 Vim。

然后使用 mkvirtualenv 创建一个新的虚拟环境:

$ mkvirtualenv scrapy_env

现在,你应该能在终端行的开头看到 (scrapy_env) 的附加信息。

要退出虚拟环境,请键入 $ deactivate

要返回 scrappy_env 虚拟环境,请键入 $ workon scray_env

安装 Scrapy

你可以使用 pip 软件包管理器安装 Scrapy:

$ pip install scrapy

这将安装最新版本的 Scrapy。

你可以使用 scrapy startproject 命令创建一个新项目:

$ scrapy startproject myproject

这将初始化一个名为 "myproject "的新 Scrapy 项目。它应包含默认的项目结构。

了解文件夹结构

这是默认的项目结构:

myproject

├── myproject

│ ├── __init__.py

│ ├─ items.py

│ ├─ middlewares.py

│ ├─ pipelines.py

│ ├─ settings.py

│ └── spiders

│ └─ __init__.py

└─ scrapy.cfg

items.py

items.py是提取数据的模型。该模型将用于存储从网站提取的数据。

例如

import scrapy

class Product(scrapy.Item):

name = scrapy.Field()

price = scrapy.Field()

description = scrapy.Field()

在这里,我们定义了一个名为 "产品 "的项目。Spider 可以用它来存储产品的名称、价格和描述等信息(参见 /spiders)。

/蜘蛛

/spiders 是一个包含Spider类的文件夹。在 Scrapy 中,Spiders 是定义网站刮擦方式的类。

例如

import scrapy

from myproject.items import Product

class MySpider(scrapy.Spider):

name = 'myspider'

start_urls = ['<example_website_url>']



def parse(self, response):

# Extract the data for each product

for product_div in response.css('div.product'):

product = Product()

product['name'] = product_div.css('h3.name::text').get()

product['price'] = product_div.css('span.price::text').get()

product['description'] = product_div.css('p.description::text').get()

yield product

蜘蛛 "通过 start_urls,提取页面上所有产品的名称、价格和描述(使用 css 选择器),并将数据存储在产品项目中(见 items.py)。然后,它 "产生 "这些项目,使 Scrapy 将它们传递给管道中的下一个组件(见 pipelines.py)。

Yield 是 Python 中的一个关键字,它允许函数在不结束函数的情况下返回值。相反,它会产生一个值并暂停函数的执行,直到请求下一个值。

例如

def count_up_to(max):

count = 1

while count <= max:

yield count

count += 1

for number in count_up_to(5):

print(number)

// returns 1 2 3 4 5 (each on a new line)

pipelines.py

管道 负责处理蜘蛛提取的项目(参见 items.py 和 /spiders)。您可以使用它们来清理 HTML、验证数据并将其导出为自定义格式或保存到数据库中。

例如

import pymongo

class MongoPipeline(object):

def __init__(self):

self.conn = pymongo.MongoClient('localhost', 27017)

self.db = self.conn['mydatabase']

self.product_collection = self.db['products']

self.other_collection = self.db['other']



def process_item(self, item, spider):

if spider.name == 'product_spider':

//insert item in the product_collection

elif spider.name == 'other_spider':

// 在 other_collection 中插入项目

return item

我们创建了一个名为 MongoPipeline 的管道。它连接到两个 MongoDB 集合(product_collection 和 other_collection)。管道从蜘蛛(参见 /spiders)接收项目(参见 items.py),并在 process_item 函数中对其进行处理。在本例中,process_item 函数将项目添加到指定的集合中。

settings.py

settings.py存储了控制 Scrapy 项目行为的各种设置,例如应使用的管道、中间件和扩展,以及与项目应如何处理请求和响应相关的设置。

例如,可以用它来设置管道的执行顺序(参见 pipelines.py):

ITEM_PIPELINES = {

'myproject.pipelines.MongoPipeline': 300,

'myproject.pipelines.JsonPipeline': 302,

}

// MongoPipeline will execute before JsonPipeline, because it has a lower order number(300)

或设置出口容器:

FEEDS = {

'items': {'uri': 'file:///tmp/items.json', 'format': 'json'},

}

// this will save the scraped data to a items.json

scrappy.cfg

scrapy.cfg 是项目主要设置的配置文件。

[settings]

default = [name of the project].settings

[deploy]

#url = http://localhost:6800/

project = [name of the project].

middlewares.py

Scrapy 中有两种中间件:下载器中间件和蜘蛛中间件。

下载器中间件是可用于修改请求和响应、处理错误以及实现自定义下载逻辑的组件。它们位于蜘蛛和 Scrapy 下载器之间。

蜘蛛中间件是可以用来实现自定义处理逻辑的组件。它们位于引擎和蜘蛛之间。

废旧外壳

在我们开始实施 Urban Dictionary 搜刮程序这一激动人心的旅程之前,我们应该先熟悉一下 Scrapy Shell。通过这个交互式控制台,我们可以测试我们的搜刮逻辑,并实时查看结果。它就像一个虚拟沙盒,我们可以在这里尽情玩耍,并在将我们的 "蜘蛛 "释放到网络上之前对我们的方法进行微调。相信我,从长远来看,这将为你节省大量的时间和精力。那么,就让我们来体验一下 Scrapy Shell 的乐趣吧。

打开外壳

要打开 Scrapy Shell,首先需要在终端导航到 Scrapy 项目的目录。然后,运行以下命令即可:

scrapy shell

这将打开 Scrapy Shell,你将看到一个提示,在此输入并执行 Scrapy 命令。你也可以把 URL 作为参数传递给 Shell 命令,直接抓取网页,就像这样:

scrapy shell <url>

例如

scrapy shell https://www.urbandictionary.com/define.php?term=YOLO

将返回(在响应对象中)包含 YOLO(在 Urban Dictionary 中)定义的网页 html。

另外,进入 shell 后,还可以使用 fetch 命令获取网页。

fetch('https://www.urbandictionary.com/define.php?term=YOLO')

也可以使用 nolog 参数启动 shell,使其不显示日志:

scrapy shell --nolog

使用外壳

在本例中,我获取了 "https://www.urbandictionary.com/define.php?term=YOLO "URL,并将 html 保存到 test_output.html 文件中。

(scrapy_env) mihai@DESKTOP-0RN92KH:~/myproject$ scrapy shell --nolog

[s] Available Scrapy objects:

[s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc)

[s] crawler <scrapy.crawler.Crawler object at 0x7f1eef80f6a0>

[s] item {}

[s] settings <scrapy.settings.Settings object at 0x7f1eef80f4c0>

[s] Useful shortcuts:

[s] fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)

[s] fetch(req) Fetch a scrapy.Request and update local objects

[s] shelp() Shell help (print this help)

[s] view(response) View response in a browser

>>> response // response is empty

>>> fetch('https://www.urbandictionary.com/define.php?term=YOLO')

>>> response

<200 https://www.urbandictionary.com/define.php?term=Yolo>

>>> with open('test_output.html', 'w') as f:

... f.write(response.text)

...

118260

现在,让我们检查 test_output.html,并确定我们需要的选择器,以便为我们的 Urban Dictionary scraper 提取数据。

博客图片

我们可以观察到这一点:

  • 每个词语定义容器都有 "定义 "类。
  • 单词的含义可在 div 中的 "meaning "类中找到。
  • 单词的示例可在 div 中的 "example "类中找到。
  • 有关帖子作者和日期的信息可在 "contributor "类 div 中找到。

现在,让我们在 Scrapy Shell 中测试一些选择器:

要获取每个定义容器的引用,我们可以使用 CSS 或 XPath 选择器:

有关 XPath 选择器的更多信息,请访问:https://www.webscrapingapi.com/the-ultimate-xpath-cheat-sheet。

definitions = response.css('div.definition')
definitions = response.xpath('//div[contains(@class, "definition")]')

我们应该从每个定义容器中提取含义、示例和发布信息。让我们用第一个容器测试一些选择器:

>>> first_def = definitions[0]

>>> meaning = first_def.css('div.meaning').xpath(".//text()").extract()

>>> meaning

['Yolo ', 'means', ', '', 'You Only Live Once', ''.']

>>> meaning = "".join(meaning)

>>> meaning

'Yolo means, 'You Only Live Once'.'

>>> example = first_def.css('div.example').xpath(".//text()").extract()

>>> example = "".join(example)

>>> example

'"系上安全带"。Jessica 说.\r "HAH, YOLO!"'

>>>post_data=first_def.css('div.contributor').xpath(".//text()").extract()

>>>post_data

['by ', 'Soy ugly', ' April 24, 2019'].

通过使用 Scrapy shell,我们很快就找到了适合我们需要的通用选择器。

definition.css('div.<meaning|example|contributor>').xpath(".//text()").extract() 

// returns an array with all the text found inside the <meaning|example|contributor>

ex: ['Yolo ', 'means', ', '', 'You Only Live Once', ''.']

要了解有关 Scrapy 选择器的更多信息,请查阅文档。https://docs.scrapy.org/en/latest/topics/selectors.html。

实施 Urban Dictionary scraper

干得好现在,你已经掌握了 Scrapy Shell 的使用窍门,并了解了 Scrapy 项目的内部运作,是时候深入到我们的 Urban Dictionary scraper 的实现中去了。现在,你应该已经信心十足,准备好从网络中提取所有搞笑(有时是有问题的)的词语定义了。话不多说,让我们开始构建我们的搜索器吧!

定义项目

首先,我们将实现一个项目:(参见 items.py)

class UrbanDictionaryItem(scrapy.Item):

meaning = scrapy.Field()

author = scrapy.Field()

date = scrapy.Field()

example = scrapy.Field()

该结构将保存从 Spider 抓取的数据。

定义蜘蛛

这就是我们定义蜘蛛的方式(参见 /spiders):

导入 scrapy

从 .items 导入 UrbanDictionaryItem

类 UrbanDictionarySpider(scrapy.Spider):

name = "urban_dictionary

start_urls = ['https://www.urbandictionary.com/define.php?term=Yolo']

def parse(self, response):

definitions = response.css('div.definition')

为定义中的定义:

item = UrbanDictionaryItem()

item['meaning'] = definition.css('div.meaning').xpath(".//text()").extract()

item['example'] = definition.css('div.example').xpath(".//text()").extract()

author = definition.css('div.contributor').xpath(".//text()").extract()

item['date'] = author[2]

item['author'] = author[1]

产量项

要运行 urban_dictionary 蜘蛛,请使用以下命令:

scrapy crawl urban_dictionary

// 结果应显示在控制台中(很可能在日志的最上方)

创建管道

此时,数据是未消毒的。

{'author': 'Soy ugly',

'date': ' April 24, 2019',

'example': ['“Put your ',

'seatbelt',

' on.” Jessica said.\n',

'“HAH, YOLO!” Replies Anna.\n',

'(They then proceed to have a ',

'car crash',

'. ',

'Long story short',

'...Wear a seatbelt.)'],

'meaning': ['Yolo ', 'means', ', ‘', 'You Only Live Once', '’.']}

我们要修改 "示例 "和 "含义 "字段,使其包含字符串,而不是数组。为此,我们将编写一个管道(见 pipelines.py),通过连接单词将数组转换为字符串。

class SanitizePipeline:

def process_item(self, item, spider):

# 净化 "含义 "字段

item['meaning'] = "".join(item['meaning'])

# 净化 "示例 "字段

item['example'] = "".join(item['example'])



# 净化 "日期 "字段

item['date'] = item['date'].strip()

return item

//ex: ['Yolo ', 'means', ', '', 'You Only Live Once', ''.'] 转为

'Yolo means, 'You Only Live Once'.

启用管道

定义管道后,我们需要启用它。如果不这样做,我们的 UrbanDictionaryItem 对象就不会被净化。为此,请将其添加到 settings.py 文件中(参见 settings.py):

 ITEM_PIPELINES = {

'myproject.pipelines.SanitizePipeline': 1,

}

同时,您还可以指定一个输出文件,以便将刮擦数据放入其中。

 FEEDS = {

'items': {'uri': 'file:///tmp/items.json', 'format': 'json'},

}

// this will put the scraped data in an items.json file.

可选:实施代理中间件

网络抓取可能有点麻烦。我们经常遇到的一个主要问题是,许多网站需要 javascript 渲染才能完全显示其内容。这会给我们网络搜刮者带来巨大的麻烦,因为我们的工具通常无法像普通网络浏览器那样执行 javascript。这可能导致提取的数据不完整,或者更糟的是,我们的 IP 会因为在短时间内发出过多请求而被网站禁止访问。

我们的解决方案是 WebScrapingApi。有了我们的服务,您只需向 API 提出请求,它就会为您处理所有繁重的工作。它将执行 JavaScript、旋转代理,甚至处理验证码,确保您可以轻松刮取最顽固的网站。

代理中间件会将 Scrapy 发出的每个获取请求转发给代理服务器。然后,代理服务器会替我们发出请求,并返回结果。

首先,我们要在 middlewares.py 文件中定义一个 ProxyMiddleware 类。

import base64

class ProxyMiddleware:

def process_request(self, request, spider):

# Set the proxy for the request

request.meta['proxy'] = "http://proxy.webscrapingapi.com:80"

request.meta['verify'] = False

# Set the proxy authentication for the request

proxy_user_pass = "webscrapingapi.proxy_type=residential.render_js=1:<API_KEY>"

encoded_user_pass = base64.b64encode(proxy_user_pass.encode()).decode()

request.headers['Proxy-Authorization'] = f'Basic {encoded_user_pass}'

在本例中,我们使用了 WebScrapingApi 代理服务器。

webscrapingapi.proxy_type=residential.render_js=1 is the proxy authentication username, <API_KEY> the password.

您可以在https://www.webscrapingapi.com/上创建新账户,免费获取 API_KEY

定义 ProxyMiddleware 后,我们只需在 settings.py 文件中将其作为 DOWNLOAD_MIDDLEWARE 启用即可。

DOWNLOADER_MIDDLEWARES = {

'myproject.middlewares.ProxyMiddleware': 1,

}

就是这样。正如你所看到的,使用 Scrapy 进行网络搜刮可以简化我们的许多工作。

总结

我们成功了!我们创建了一个能从 Urban Dictionary 中提取定义的 scraper,在这过程中,我们学到了很多关于 Scrapy 的知识。从创建自定义项目到利用中间件和管道,我们已经证明了使用 Scrapy 进行网络搜刮的强大功能和多样性。最棒的是什么?还有更多精彩等着我们去发现。

恭喜你和我一起完成了这次旅程。现在,你应该对自己的能力充满信心,能够胜任任何网络搜索项目。请记住,网络搜索并不一定令人生畏或难以承受。有了正确的工具和知识,它可以成为一种有趣而有意义的体验。如果您需要帮助,请不要犹豫,联系我们https://www.webscrapingapi.com/,我们非常了解网络搜索,非常乐意为您提供任何帮助。

新闻和更新

订阅我们的时事通讯,了解最新的网络搜索指南和新闻。

We care about the protection of your data. Read our <l>Privacy Policy</l>.Privacy Policy.

相关文章

缩图
指南如何抓取亚马逊产品数据:最佳实践与工具综合指南

通过我们的深入指南,探索刮擦亚马逊产品数据的复杂性。从最佳实践和亚马逊 Scraper API 等工具到法律注意事项,了解如何应对挑战、绕过验证码并高效提取有价值的见解。

Suciu Dan
作者头像
Suciu Dan
15 分钟阅读
缩图
网络抓取科学Scrapy 与 Selenium:选择最佳网络抓取工具综合指南

探索 Scrapy 和 Selenium 在网络刮擦方面的深入比较。从大规模数据采集到处理动态内容,了解两者的优缺点和独特功能。了解如何根据项目需求和规模选择最佳框架。

WebscrapingAPI
作者头像
WebscrapingAPI
14 分钟阅读
缩图
指南Scrapy Splash 教程:掌握使用 Scrapy 和 Splash 抓取 JavaScript 渲染的网站的艺术

学习如何使用 Scrapy 和 Splash 抓取 JavaScript 渲染的动态网站。从安装到编写 spider、处理分页和管理 Splash 响应,本综合指南为初学者和专家提供了循序渐进的指导。

Ștefan Răcila
作者头像
Ștefan Răcila
6 分钟阅读