一、前言
在工作的这么多年中,其实有很少能接触到挑战自我的项目。在小公司当个小组长,无非就是curd、部署项目、搭建gitlab、review同事代码等繁琐的工作,偶尔写写前端。大公司的话,工作内容其实就没这么繁琐了,部署项目有运维,页面有前端同事,review有部门leader,工作中80%的时间都是curd,当然会有一些小型的基于需求的项目来做,但都是能力范围之内的。
在我这短暂的开发生涯中,还真就遇到过那么一次让我觉得非常有挑战性的事,那就是从php转python,从0到1实现爬虫架构。从结果上来说,虽然达不到100%的爬取成功率,但整个过程也可以说是倾尽了全力,当然这也是我为数不多的我绞尽脑汁想完成的项目(主要是这个项目完成之后的绩效比较诱人,咳咳咳~),而且我对新东西、新事物都有种想挑战一下的心态。
今天就来讲一下我是怎么从0到1实现整个爬虫架构的。
首先声明:我是主导者、也是参与者,当然后期还有更专业的python同事一起加入,帮我调优,给我优化意见,最终才合力完成的这个项目,不是我一个人的功劳。
二、起因
在加入C公司一年后,到了年终总结阶段,项目经理要找团队的每个人聊过去的一年中主要的业绩和贡献,其实就是谈绩效了。在对话中我清晰的记得,当项目经理问到我
:“对自身有什么规划或者对自己在团队内的职责有什么想说的”
的时候,我脑海中一个想法已经酝酿了好久,我说
:“我想多做一些有挑战性的事情”
其实不太敢明说,目前在工作中的事情太顺风顺水了,不怎么用点脑子就能完成(因为主要负责的是公司内部系统的开发,所以高并发、分布式什么的几乎接触不到)
就是这样一句话在不久后起了波澜
公司主要是做跨境电商的,在亚马逊平台销售自己的产品。既然是电商行业,那就少不了要爬取竞品数据,分析对手的产品、评价、销量等,这时候就需要爬虫出马了。上层有了这个决定之后,落实下来就到了我们小组身上了。
最开始是计划用php做爬虫的,但是后面了解到爬虫不是php的长处,python才是。而且python提供非常完整的生态链来处理爬虫。下面我列举下我当时了解到python为什么适合爬虫的原因,而且也是我后面说服leader用python做爬虫的理由:
三、Python优势
1、语言层面的优势
字符串处理能力
原生支持正则表达式(re模块),举个例子:
python中获取页面中的单价
import re
re.findall(r'price:(\d+)', html) # 一行代码提取价格
开发效率
动态类型 + 丰富的内置数据结构(字典/列表/集合),可以更快的编写爬虫代码
再举个例子,python提取a标签中的href
links = [a['href'] for a in soup.select('a')]
php实现
$doc = new DOMDocument();
@$doc->loadHTML($html); // @抑制解析警告
$links = array();
$anchorTags = $doc->getElementsByTagName('a');
foreach ($anchorTags as $a) {
$href = $a->getAttribute('href');
if (!empty($href)) {
$links[] = $href;
}
}
// 输出结果
print_r($links);
python一行解决的问题,php需要8行。哪个便利一目了然吧
2、生态工具链
工具 | 作用 | 优势 |
---|---|---|
Requests | HTTP请求 | 人类友好的API,会话保持自动处理 |
BeautifulSoup | HTML解析 | 容错性强,find_all比xpath更直观 |
Scrapy | 全功能爬虫框架 | 内置异步、中间件、管道等完整架构 |
Selenuim | 动态页面渲染 | 模拟浏览器行为 |
PyQuery | JQuery式选择器 | 对前端开发者更友好 |
应对爬虫的灵活性
快速切换解析方案
# 应对网站改版,快速更换解析方式
def parse(html):
try:
return css_select(html)
except ParseError:
return xpath_parse(html) # 备用方案
动态执行js
# 使用PyExecJS执行加密算法
import execjs
ctx = execjs.compile("""
function decrypt(t) {
return CryptoJS.AES.decrypt(t, 'key').toString();
}
""")
decrypted = ctx.call("decrypt", encrypted_str)
IP轮换便捷性
# 使用代理中间件示例(Scrapy)
class ProxyMiddleware:
def process_request(self, request, spider):
request.meta['proxy'] = random.choice(proxy_pool)
3、大数据处理
分布式扩展
Scrapy-Redis 实现分布式爬虫
celery调度异步任务
无缝对接数据管道
# 爬取后直接进入分析流程
import pandas as pd
data = pd.DataFrame(scraped_items)
data.groupby('category').sum().plot(kind='bar')
基于以上优势,在当时做爬虫用python可以说是最好的选择了。
四、学习
那确定了语言,现在就开始准备了,团队内没有会python的咋整?
学呗!
我和团队内的另外一个php同事开始自学起了python,首先确定学习路线
python语法
python爬虫框架scrapy
Xpath语法
反爬
代理
就这样用了一个月时间,基本上把这些东西都过了一遍,一个月之间我总结了一些关于python及爬虫的知识:
后面又整理了反爬的套路及应对措施:Python爬虫研究 - 命中水
以上都是接口爬虫,后面了解到需要爬页面,然后又学习了页面爬虫
在学习的过程中,尝试爬了自己的网站,源码在Python scrapy demo: Python scrapy框架实战demo
这一个月中走到哪都是我学习爬虫的身影,地铁上刷B站视频、晚上回家学习python语法、周末自己练习爬爬简书啥的,甚至做梦都梦到爬虫成功了。现在回忆起来真是佩服我自己
五、简单实战
接下来,产品经理需求下来了,根据亚马逊商品ASIN爬评论,看看学习成果咋样(一个月中另一个php同事没啥进展,后面就我自己开发了)
基于scrapy开发的爬虫脚本
初始化爬取链接
xpath获取关键数据
数据通过管道写入mongodb
基于scrapy我很快完成了ASIN评论的爬取,但是因为评论过多,有时候爬取到一两页就显示爬取失败了,因为我没加代理,然后选择了快代理作为我们最开始的代理方(没有建立自己的代理ip库是因为当时自己参考过一版别人的源码,做出来的效果发现那些免费的代理ip质量都不太高,所以后面就选择了收费的),快代理有多种模式可选择,具体可以去官网参考下快代理 - 企业级HTTP代理IP云服务_专注IP代理11年
既然方案没问题,那就开始大刀阔斧的干了
六、着手开发
需求是提前往内部系统里录ASIN,然后每天定时根据这些ASIN爬取评论数据,重复的替换掉
OK,既然需求定了,那就开始整理下设计架构了(这时候公司招的专业的python同事入场了)经过了多次的沟通,整理架构如下:
于此,框架基本完成,主要增加:
使用scrapy-redis分布式爬虫增加爬取效率
接口API使用Flask框架
接口采用celery异步队列
关键问题
过了一段时间, 此架构基本落地了,但是遇到个问题,就算使用了专业的ip代理工具,但是有时候爬取质量和效率还不是那么好。不得不说,亚马逊的反爬机制还是挺厉害的。基于此前提下,我们产生了更换ip代理商的想法,于是我们发现了scrapy_crawlera。
Scrapy-Crawlera
Scrapy-Crawlera 是一个 Scrapy 中间件,用于与 Crawlera(Smart Proxy)服务集成,帮助爬虫开发者高效处理反爬机制(如 IP 封禁、验证码、动态渲染等)。Crawlera 是 Zyte(原 Scrapinghub)提供的 智能代理服务,可以自动管理代理池、处理请求头、执行 JavaScript 渲染等
Crawlear的核心功能
智能代理轮换
自动切换 全球代理 IP(覆盖 100+ 国家)
避免 IP 被封,支持高并发请求
自动重试失败的请求
自动反反爬
动态调整请求头(User-Agent、Referer、Cookies)
处理 JavaScript 渲染(类似无头浏览器)
绕过 Cloudflare、Akamai 等防护
请求优化
自动管理会话(Session)
支持 HTTP/HTTPS/SOCKS5
提供 请求统计(成功率、延迟、封禁率)
安装与配置
pip install scrapy-crawlera
scrapy配置
# 启用 Crawlera 中间件
DOWNLOADER_MIDDLEWARES = {
'scrapy_crawlera.CrawleraMiddleware': 300
}
# 设置 Crawlera API Key(需注册 Zyte 获取)
CRAWLERA_ENABLED = True
CRAWLERA_APIKEY = '你的API_KEY'
# 调整爬虫并发(Crawlera 默认限制 5 并发)
CONCURRENT_REQUESTS = 32
CONCURRENT_REQUESTS_PER_DOMAIN = 32
AUTOTHROTTLE_ENABLED = False # 关闭自动限速
适用场景
企业级爬虫(需要高稳定性)
反爬严格的网站(如电商、社交媒体)
不想管理代理池(减少运维成本)
使用scrapy-clawlera的前提,就是需要去Zyte注册一个用户(我之前用的时候叫scrapyinghub,现在改名了),然后购买付费,按请求收费。比如200W个请求,400美刀,我记得我们巅峰时期一个月的代理费都干到过4000多人民币。但是这钱花的也值,爬取效率杠杠的!
scrapy-clawlear除了贵几乎没有缺点。
七、需求升级
过了没多久,新需求又来了,公司在亚马逊有店铺,运营同学每次都要去亚马逊后台下载报表数据,我们有7、8个店铺的数据,亚马逊要求又很严格,每个店铺、站点都只能在同一个IP登录,如果不同IP登录了店铺后台,亚马逊可能会封禁账号,到时候申诉就会很麻烦。
所以需要这不就来了,要求爬虫自动抓取后台报表数据。保存在数据库,运营在同学在内部系统直接查看即可。
因为这个是需要登录账号之后进行的操作,所以接下来的处理就是需要模仿浏览器用户行为了,也就是页面爬虫。
说起页面爬虫,那就不得不提到selenimu了
selenium
Selenium 是一个用于 自动化 Web 浏览器操作 的开源工具集,主要用于 Web 应用程序测试 和 浏览器自动化。它支持多种编程语言(如 Python、Java、C#、JavaScript 等),可以模拟用户在浏览器中的操作,如点击、输入、导航等。
通俗点来讲就是你可以编写程序来模仿浏览器的用户行为,比如有个网站有个签到功能,每天签到可以得积分,然后你就可以用程序模拟这个动作,每天打开网页,在指定位置,点击签到。这个行为用户可以是无感知的,用户就算在操作电脑,也不会发现你运行了脚本,打开了网页。因为这些动作都是后台运行的。
python使用selenium代码如下:
# -*- coding: utf-8 -*-
# 引入selenium webdriver类
from selenium import webdriver
# 引入火狐浏览器配置类
from selenium.webdriver import FirefoxOptions
# 实例化一个配置项
options = FirefoxOptions()
# 设置无需打开浏览器(如何想调试就把这个配置注释掉)
options.add_argument('--headless')
# 设置浏览器类型为火狐
browser = webdriver.Firefox(firefox_options=options)
# 打开一个网址
browser.get('https://item.jd.com/100008348542.html')
# 获取网页源码
source = browser.page_source
print(source)
具体如何使用详情参考我之前的文章:Python如何爬取动态网站? - 命中水
除了selenium之外呢,python还有另外一种方式也可以获取页面上的数据
Splash
Splash 是一个基于 Lua 的轻量级 JavaScript 渲染服务,由 Scrapy 团队开发,主要用于 动态网页的抓取。它通过 HTTP API 或 Scrapy-Splash 中间件 与爬虫集成,能够执行 JavaScript 并返回渲染后的页面内容。
核心功能
渲染 JavaScript 动态内容(如 React、Vue 生成的页面)。
支持截图、延迟加载、自定义 Lua 脚本。
通过 Docker 快速部署,资源占用较低。
那我们到底该如何选择呢?请看下面splash和selenium的对比
Splash 和 Selenium 的区别
特性 | Splash | Selenium |
---|---|---|
定位 | 专为爬虫设计的轻量级渲染服务 | 浏览器自动化工具,兼顾测试和爬虫 |
架构 | 基于 HTTP API 的无头浏览器服务 | 直接控制本地/远程浏览器实例 |
性能 | 更高(无图形界面,资源占用少) | 较低(启动完整浏览器) |
编程语言支持 | 通过 Lua 脚本或 HTTP 请求交互 | 支持多语言(Python/Java/C#等) |
JavaScript 执行 | 高效,但功能有限(依赖 Lua 脚本) | 完整支持,可模拟用户操作 |
部署复杂度 | 需 Docker 或独立服务 | 需浏览器驱动(如 chromedriver) |
适用场景 | 大规模爬虫(高并发、无交互需求) | 需要交互的测试或爬虫(如点击登录) |
通过以上表格我们可以看出:
爬虫优先 Splash:轻量、高效、适合大规模数据采集
交互优先 Selenium:功能全面、适合测试或复杂操作
反爬严格时:Selenium 更接近真实用户,但 Splash 可通过 Lua 脚本伪装请求头。
以我们那个场景为例,涉及到用户交互的动作(登录),最终我们选择了selenium。
后续
后续就是这个需求以selenium登录,然后拿到登录用户信息,再调用报表接口查询数据。
然后将这个脚本放在windows任务计划中,每天定时去执行。数据上传到接口,保存到数据库中。
建立异常机制,页面改变或接口报错,发邮件到负责人或开发人邮箱中。
坑
哦对了,这里还有一个坑,我记得当时我离职前也都没有解决。
因为亚马逊后台登录地址的限制,每个账号只能在同一个IP登录,所以我们是部署了跟站点数量相对应的虚拟机,比如我们有8个站点账号,就部署了8个windows虚拟机。每次都要从远程桌面中登录进去,一旦代码有什么地方要调整或者定时任务要调整,那就要耗费大量的时间去一个一个登录进去操作,windows远程桌面有时候还很卡,所以每次操作都很痛苦。
至于为什么要搭建远程windows虚拟主机,是因为运营同学也是用这个方式登录后台的。
八、不足之处
虽然项目已经成功上线,并运行了一年多,但是还是有不足的。针对这个项目来说
亚马逊页面有时候会变化,过一两个月就会调整,所以有时候爬取失败了我们是后知后觉
后台页面爬虫,维护是个硬伤
针对问题1,现在来看的话我觉得可以引入以下几点:
设置警报当关键元素获取失败率超过阈值时通知
保留详细的错误日志和页面快照
定期运行测试爬虫检查关键元素是否存在
训练模型识别页面各元素位置
使用视觉定位(如Selenium截图后分析)
前几个还好说,就是费点力,代码调整+人工。后两个的话,就要再花费一番功夫了。
针对问题2,我记得当时是没有在虚拟机上搭建git的,原因也不记得了,如果要调整的话
虚拟主机搭建git,远程仓库代码更新时,自动同步更新虚拟主机代码
九、总结
以上就是我当时在接触到爬虫项目的全部过程了,真的是参与感满满了。上线的时候就是那种看着自己的孩子一步一步长大成人的感觉。
好了,以上就是本文的全部内容了,下次再见吧!~