使用 Scrapy 和 Splash 进行无限滚动抓取
下面,我将教您如何设置 Scrapy with Splash我们还可以在网站上使用 "无限滚动"、"拉入动态内容 "和 "处理常见的爬取难题 "等功能。让我们深入了解如何一步步完成这些工作。
为什么要抓取无限滚动内容?
无限滚动通常出现在电子商务网站、社交媒体平台和新闻聚合网站上,用户向下滚动时会加载更多内容。由于新内容只有在滚动操作后才会出现,因此基本的 HTML 解析器不足以对这类网站进行扫描。像 Splash 这样的无头浏览器就能发挥作用,帮助模拟滚动并加载动态内容,从而实现有效的搜刮。
Scrapy 和 Splash 的基础知识
Scrapy 是一个使用 Python 的开源网页抓取框架,以其速度快、简单和可扩展性著称。它提供了一种结构化的方式来组织代码并从网站中提取信息。
Splash 是一款无头浏览器,专为网络搜刮而设计。它可以执行 JavaScript 并渲染 HTML 页面。当与 Scrapy 集成为 Scrapy-Splash 时,它允许我们对依赖 JavaScript 加载内容的网站(如无限滚动的网站)进行搜刮。
跳过无限滚动搜索 - 获取数据
以下是 顶级数据集网站如果你的项目太复杂,又不想浪费时间,可以试试这些软件:
- Bright Data - 可定制和预建的跨行业数据集。
- Statista - 用于商业和研究的大量统计数据和报告。
- Datarade - 来自不同供应商的优质数据产品市场。
- AWS Data Exchange - 与 AWS 服务集成的第三方数据集。
- Zyte - 根据业务需求进行网络搜刮和定制数据集。
- Data & Sons - 买卖各种数据集的开放市场。
- Coresignal - 劳动力分析,提供大量与工作相关的数据。
- Oxylabs - 专业的公司数据和网络搜索服务。
- Bloomberg Enterprise Data Catalog - 供企业使用的财务数据。
- Kaggle - 用于数据科学的免费公共数据集和工具。
步骤 1:使用 Splash 设置 Scrapy
要开始使用带有 Scrapy 的 Splash,请遵循以下初始设置步骤。
1.安装 Scrapy-Splash 打开终端,安装 scrapy-splash 软件包:
pip install scrapy-splash
2.在 Docker 中运行 Splash 由于 Splash 需要 Docker 才能有效运行,因此请确保 Docker 已在您的计算机上安装并运行。使用以下命令提取 Splash Docker 映像:
docker pull scrapinghub/splash
然后启动 Splash 服务器:
docker run -it -p 8050:8050 - rm scrapinghub/splash
现在,Splash 可在 http://localhost:8050 上为您的 Scrapy spider 渲染 JavaScript。
步骤 2:编写用于滚动的 Lua 脚本
通过 Splash 的 Lua 脚本功能,您可以操作浏览器、向下滚动并等待加载新内容。下面的 Lua 脚本会滚动到页面底部,等待加载内容,并多次重复这一过程。
用于滚动的 Lua 脚本
function main(splash, args)
splash:go(args.url)
splash:wait(args.wait)
local scroll_to = splash:jsfunc(window.scrollTo)
local get_body_height = splash:jsfunc([[
function() {
return document.body.scrollHeight;
}
]])
local scroll_count = 0
for _ = 1, args.max_scrolls do
scroll_count = scroll_count + 1
scroll_to(0, get_body_height())
splash:wait(args.scroll_delay)
end
return {
html = splash:html()、
scroll_count = scroll_count
}
end
In this script:
- splash:go(args.url) 加载目标 URL。
- splash:wait(args.wait) 暂停,以便加载初始页面元素。
- for 循环会多次滚动页面,每次滚动后都会短暂等待(args.scroll_delay),以便加载新内容。
步骤 3:在 Scrapy Spider 中集成 Lua 脚本
Lua 脚本准备就绪后,下一步就是设置 Scrapy 蜘蛛来执行它。该蜘蛛会向目标网站发送一个 Splash 请求,并将 Lua 脚本传入。
蜘蛛代码
import scrapy
from scrapy_splash import SplashRequest
class InfiniteScrollSpider(scrapy.Spider):
name = 'infinite_scroll_spider'
allowed_domains = ['example.com]
start_urls = ['http://example.com/target_page']
lua_script = """
function main(splash, args)
splash:go(args.url)
splash:wait(args.wait)
local scroll_to = splash:jsfunc('window.scrollTo')
local get_body_height = splash:jsfunc([[
function() {
return document.body.scrollHeight;
}
]])
local scroll_count = 0
for _ = 1, args.max_scrolls do
scroll_count = scroll_count + 1
scroll_to(0, get_body_height())
splash:wait(args.scroll_delay)
end
return {
html = splash:html(),
scroll_count = scroll_count
}
end
"""
def start_requests(self):
yield SplashRequest(
self.start_urls[0],
self.parse、
endpoint='execute',
args={
lua_source: self.lua_script、
'wait': 2,
'scroll_delay': 1,
'max_scrolls': 8
}
)
def parse(self, response):
for item in response.css(''.item-selector'):
yield {
'name': item.css('.name::text').get()、
'price': item.css('.price::text').get()
}
说明:
- 蜘蛛会发送一个 SplashRequest,其中的 lua_source 指向 Lua 脚本。
- wait、scroll_delay 和 max_scrolls 等参数定义了脚本的滚动行为。
- 解析函数从每次滚动迭代中提取项目数据(名称和价格)。
步骤 4:处理分页和 "加载更多 "按钮
许多无限滚动页面都会使用一个隐藏的 "加载更多 "按钮,当用户滚动到底部时该按钮就会被激活。Splash 的 Lua 脚本可以通过点击出现的 "加载更多 "按钮来处理该问题。
为 "加载更多 "按钮修改 Lua 脚本
function main(splash, args)
splash:go(args.url)
splash:wait(args.wait)
local scroll_to = splash:jsfunc(window.scrollTo)
local get_body_height = splash:jsfunc([[
function() {
return document.body.scrollHeight;
}
]])
local scroll_count = 0
for _ = 1, args.max_scrolls do
scroll_count = scroll_count + 1
scroll_to(0, get_body_height())
splash:wait(args.scroll_delay)
local load_more = splash:select('.load-more-button')
if load_more then
load_more:mouse_click()
splash:wait(1)
end
end
return splash:html()
end
在这里,load_more 变量使用其选择器定位 "加载更多 "按钮。如果找到了,它就会模拟点击,等待加载内容,并重复滚动。
步骤 5:绕过反僵尸保护程序
无限滚动页面通常有反机器人措施,包括验证码、速率限制和 IP 禁止。绕过这些措施的技巧包括
- 代理轮换: 更改 IP 地址可防止被检测到。ZenRows 和 ScraperAPI 等服务只需最少的设置即可提供 IP 轮换功能。
- 用户代理轮换: 在每次请求时随机化 User-Agent 字符串,以避免被检测到。
- 无头浏览器 Splash 以无头模式运行,使您的请求看起来更像真实的用户流量。
以下是使用 ZenRows 实现旋转代理的方法:
import scrapy
class InfiniteScrollSpider(scrapy.Spider):
name = 'proxy_spider'
allowed_domains = ['example.com]
def start_requests(self):
proxy = 'http://@api.zenrows.com:8001'。
url = 'http://example.com/target_page'
yield scrapy.Request(
url,
callback=self.parse,
meta={'proxy': proxy}
)
def parse(self, response):
# parsing logic
本例将 Scrapy 配置为使用 ZenRows 作为每个请求的代理。
第 6 步:将所有内容整合在一起
以下是无限滚动 Scrapy 蜘蛛的完整代码,其中包含 Splash 集成、Lua 脚本和代理旋转:
import scrapy
from scrapy_splash import SplashRequest
class FullInfiniteScrollSpider(scrapy.Spider):
name = 'full_infinite_scroll'
allowed_domains = ['example.com]
start_urls = ['http://example.com/target_page']
lua_script = """
function main(splash, args)
splash:go(args.url)
splash:wait(args.wait)
local scroll_to = splash:jsfunc('window.scrollTo')
local get_body_height = splash:jsfunc([[
function() {
return document.body.scrollHeight;
}
]])
for _ = 1, args.max_scrolls do
scroll_to(0, get_body_height())
splash:wait(args.scroll_delay)
local load_more = splash:select('.load-more-button')
if load_more then
load_more:mouse_click()
splash:wait(1)
end
end
return splash:html()
end
"""
def start_requests(self):
yield SplashRequest(
self.start_urls[0],
self.parse、
endpoint='execute',
args={
lua_source: self.lua_script、
'wait': 2,
'scroll_delay': 1,
'max_scrolls': 10
}
)
def parse(self, response):
for item in response.css(''.item-selector'):
yield {
'name': item.css('.name::text').get()、
'price': item.css('.price::text').get()
}
结论
我发现 Scrapy 与 Splash 的结合非常有吸引力。这是一个很棒的设置,即使是最棘手的动态网站也能轻松应对。有了 Splash,我可以处理 JavaScript 渲染,而 Scrapy 则擅长数据提取部分。Splash 的 Lua 脚本可以与页面上的元素进行交互,加载额外的内容,就像用户滚动浏览一样。