10分钟上手搭建爬虫服务

本文由博主最初发布于华为开发者社区,原地址:https://portal.huaweicloud.com/blogs/46c0ffaad0ee11e7b8317ca23e93a891,再次感谢华为对文章的认可与鼓励 😂

爬虫是时下十分热门的一种程序,谷歌、百度等搜索引擎以及今日头条、即刻等热门应用均建立在爬虫程序的基础上,构成互联网巨大流量的入口。那么现代的爬虫是如何工作,我们自己又如何借助华为云服务搭建自己的爬虫呢?下面我们以爬取华为开发者社区所有的博客为例,利用时下热门的 PySpider 框架快速搭建一个基本的爬虫服务。

爬虫的基本原理

如果把互联网看作是各个站点相互引用、串联形成的一张网,那爬虫顾名思义就是在这张网上穿梭的蜘蛛(Spider)。一个页面作为互联网中的一个节点,很可能会包含有指向其他页面的链接,可以理解为节点之间的连线。爬虫通过遍历节点之间的连线,确定页面之间的网络结构,抓取所需要的信息。

在开始之前,我们应该清楚一些基本的概念:

  • WWW (World Wide Web, 万维网) 是一个由互相连接的超文本页面组成的系统

  • 每个页面有对应的 URL(Uniform Resource Locator, 网址) 所标记

  • 页面通过 HTTP (Hypertext Transfer Protocol, 超文本传输协议) 传输

  • 网页使用 HTML(HyperText Markup Language, 超文本标记语言) 表示其页面结构

那么具体而言,爬虫程序的运行过程是:

  1. 寻找包含所需信息的页面 URL

  2. 通过 HTTP 获取页面

  3. 从 HTML 中解析出信息

  4. 从中发现更多包含所需信息的 URL,跳转到步骤2

在实现层面上,爬虫程序运行之初,需要提供给它一个 URL 列表,引导爬虫从这些页面开始访问。当爬虫访问这些 URL 时,会识别出页面中的所需的 URL,并将这些 URL 加入到待访问的 URL 列表中。URL 列表中的地址会以一系列策略递归地被爬虫所访问,爬虫会记录这些地址所对应的页面以及一些页面信息。

现代爬虫的基本结构

PySpider 简介

PySpider 是一个基于 Python 开发的强大爬虫系统,它具有以下特点:

  • 用户使用 Python 编写脚本以控制爬虫的工作流程

  • 包含基于 WebUI 的可视化脚本编辑器、任务监视器、项目管理器及结果预览工具

  • 可以使用 MySQL、MongoDB、Redis、SQLite、ElasticSearch、PostgreSQL 等工具结合 SQLAlchemy 作为存储后端

  • 可以使用 RabbitMQ、Beanstalk、Redis 和 Kombu 作为消息队列

  • 支持任务优先级、任务重试、周期任务、根据页面寿命自动重爬等高级功能

  • 分布式架构,支持 js 解析,支持 Python2 与 Python 3

总结一下,利用 PySpider,可以方便地搭建出专业的爬虫框架。

实战操作

1. 配置华为云服务

作为一个专业稳定的爬虫服务,而不是简单批量下载页面的工具,我们往往需要搭建一个服务器,来让它自动地、定期地去工作,这时一个稳定可信赖的云服务就至关重要了,我们这里就以业界领先的华为云服务为例,搭建爬虫的基础运行环境。

我们首先需要在 https://console.huaweicloud.com/ecm/?locale=zh-cn#/ecs/createVm 申请一个台云服务器。

我的服务器配置为

服务器配置

这里我选择了个人更偏好的 CentOS 7.3 系统,下面就以 CentOS 7.3 系统为例,继续接下来的搭建。

2. 环境准备

在安装 PySpider 前,我们可以做一些环境准备,来提升服务的稳定性,这也是在搭建服务时的好习惯。

首先更新 yum,这步会比较漫长

yum update -y

安装 EPEL(Extra Packages for Enterprise Linux),用以安装下面需要的包

yum install epel-release

安装依赖库

yum install python-pip python-devel libxml2-devel python-lxml libxslt-devel openssl-devel -y

升级 pip

pip install --upgrade pip

至此,运行环境及必需的依赖已升级至最新版本。下面我们根据喜好进行一些可选的配置,首先安装 MariaDB 作为爬虫的后端数据库。

yum install mariadb-server mariadb -y

启动 MariaDB 服务

systemctl start mariadb

如果有需要,可以设置数据库 root 用户的密码(your_password 应替换为你的密码),当然也可以不设置:

mysqladmin -u root password "your_password"

这时可以使用如下命令来检查 MariaDB 是否配置成功(如果未设置密码,直接使用 mysql 即可):

mysql -u root -p

然后输入刚才设置的密码,如果没有问题,应该可以看到以 MariaDB [(none)]> 的提示。

此时输入 SHOW DATABASES; 语句,查看所有数据库,如果有类似下面的输出,则配置正常。

MariaDB [(none)]> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+
4 rows in set (0.00 sec)

输入 exit 退出,进入下一步。

接下来,安装 Redis,作为消息队列使用。

yum install redis -y

在启动 Redis 之前,需要做一些设置,让 Redis 作为一个服务运行。首先创建一份配置文件:

mkdir -p /etc/redis
cp /etc/redis.conf /etc/redis/redis.conf

修改 /etc/redis/redis.conf ,将其中的 daemonize 项修改为 yes

daemonize yes

配置文件路径作为参数,启动 Redis 服务:

redis-server /etc/redis/redis.conf

3. 部署PySpider

在安装 PySpider 之前,需要先安装 mysql-connector 及 redis 两个依赖,而最新的 mysql-connector 2.2 有 protobuf 的依赖,protobuf 笔者目前还没找到合适的安装方法,所以这里退而求其次,安装不依赖 protobuf 的 mysql-connector 2.1.6

pip install mysql-connector==2.1.6 redis

安装 PySpider

pip install pyspider

创建 PySpider 配置目录

mkdir /etc/pyspider

/etc/pyspider 目录下创建 pyspider.conf.json 文件,以下可作为参考:

{
  "taskdb": "mysql+taskdb://root:your_password@localhost:3306/taskdb",
  "projectdb": "mysql+projectdb://root:your_password@localhost:3306/projectdb",
  "resultdb": "mysql+resultdb://root:your_password@localhost:3306/resultdb",
  "message_queue": "redis://localhost:6379/db",
  "webui": {
    "username": "sunny",
    "password": "your_user_password",
    "need-auth": true
  }
}

其中 taskdb, projectdbresultdb 分别为任务、项目及结果的数据库连接地址,这里我们用一个 MariaDB 服务存储这三类数据,上面的 root 为我们的 MariaDB 用户名,后面 your_password 应替换为之前设置的数据库密码。 message_queue 后应填写消息队列服务的地址,这里我们是将 Redis 做为消息队列使用。 webui 项中的 usernamepassword 是用来配置访问 WebUI 时的用户名和密码,也可以不设置用户名和密码,只需将 need-auth 改成 false 即可。

接下来创建一个工作文件夹,并启动爬虫

mkdir ~/pyspider
cd ~/pyspider
pyspider -c /etc/pyspider/pyspider.conf.json

此时访问 http://你的服务器IP地址:5000,应该可以看到 PySpider 的 Dashboard。

如果访问失败,很可能是防火墙组织了发往5000端口的数据包,这时我们需要打开5000端口

iptables -A INPUT -p tcp --dport 5000 -j ACCEPT
iptables --flush

此时重新启动爬虫,就可以访问 Dashboard 了,我们也可以结合 nohup 让爬虫服务运行在后台:

nohup pyspider -c /etc/pyspider/pyspider.conf.json &

至此 PySpider 服务的部署就完成了。

4. 爬取华为开发者社区

在搭建好服务之后,我们的目标是建立一个华为开发者社区所有文章的数据库,也就是要保存社区中所有的博客文章以及相关的数据。既然要对这些文章下手,那第一件事就是需要找到一个合适的文章列表。一个理想的列表应该具有这些特征:

  • 尽可能多地包含指向文章页面的链接

  • 可以通过跳转到下一页获取到所有文章的 URL

  • 列表根据时间由新到旧排序,方便获取到最新的文章

很幸运,我们发现社区博客页面的首页就有这样一个我们所期待的列表

博客首页

https://portal.huaweicloud.com/blogs

接下来我们正式开始上手 PySpider。首先输入之前设置的用户名和密码进入 Dashboard,然后点击 Create 新建一个 Project,命名为 huawei_developer,初始 URL 填写我们刚刚找到的列表页面https://portal.huaweicloud.com/blogs,类型选择 Script

创建 Project

右侧是我们写爬虫代码的编辑区,在 PySpider 中,on_start是整个爬虫的入口

@every(minutes=24 * 60)
def on_start(self):
    self.crawl('https://portal.huaweicloud.com/blogs', callback=self.index_page)

self.crawl 会获取页面,并调用 callback 来分析响应,@every 装饰器表示 on_start 会每天运行一次确保不会漏掉新的文章。

点击绿色的 Run 按钮,切换到 follow 页面,下面会出现一个链接,就是我们这里首先要爬取的第一个页面,我们这里点击绿色三角按钮

爬取首页

在首页上,我们需要提取两种链接:一是文章的链接,比如https://portal.huaweicloud.com/blogs/b16fc680d01811e7b8317ca23e93a891;二是列表翻到下一页的链接。

我们可以看到,在爬取首页后,默认设置的爬虫在页面上找出了127条链接。而其中我们想要的页面链接实际上只存在于文章列表的区域,下面我们将使用类似于 CSS 选择器的工具来过滤出我们想要的元素。

CSS 选择器是 CSS 用来选择 HTML 中元素的工具,CSS 通过这种简单的语法指定特定的元素并应用一些样式。由于包含信息的元素往往具有不同的样式,所以这里使用 CSS 选择器来过滤元素非常合适。关于 CSS 选择器的更多信息可以参考:

https://www.w3schools.com/cssref/css_selectors.asp

这里我们可以通过 PyQuery 内建的 response.doc 对象使用 CSS 选择器。

PySpider 提供了一个叫做 css selector helper 的工具,利用它我们可以更方便地通过点击元素得到它对应的 css 选择器。切换到 web 页面并点击 enable css selector helper 就可以使用它。

css selector helper

当光标移到元素上时,对应元素会变成橙色高亮状态。点击元素,一条CSS 选择器信息会出现在页面顶部,你可以进一步编辑来定位需要的元素,然后把 CSS 选择器加在你的代码里。

我们点击一条带链接的文章标题,然后将选择器加在代码里:

@config(age=10 * 24 * 60 * 60)
def index_page(self, response):
    for each in response.doc('a.common-blog-title').items():
        self.crawl(each.attr.href, callback=self.detail_page)

在提取到文章链接的同时,我们也要考虑翻页操作。这里可以用同样的思路得到指向下一页的链接。值得注意的是,最后一页的页面上没有对应的翻页控件,因此需要做一个额外的判断,在有翻页按钮的情况下才向后翻页。此外,我们解析文章列表页的函数是 index_page 本身,所以翻页的回调函数应设为 self.index_page,修改后的代码为:

@config(age=10 * 24 * 60 * 60)
def index_page(self, response):
    for each in response.doc('a.common-blog-title').items():
        self.crawl(each.attr.href, callback=self.detail_page)
    next = response.doc('.ucd-pager-next > a')
    if next:
        self.crawl(next.attr.href, callback=self.index_page)

再次点击 run,然后进入 follow 页,选择一个文章链接,点击三角进入详情。

我们可以加一些键值来提取和保存更多的信息:

@config(priority=2)
def detail_page(self, response):
    return {
        "url": response.url,
        "title": response.doc('.cloud-blog-detail-title').text(),
        "author": response.doc('.sub-content-username').text(),
        "release_time": response.doc('.article-write-time').text()[4:],
        "view_time": response.doc('.common-blog-eye').next().text(),
        "comment_time": response.doc('.common-blog-bubbling').next().text(),
        "tags": [i.text() for i in response.doc('.blog-menu-footer > a').items()],
        "summary": response.doc('.cloud-blog-detail-summary-tag').text(),
        "content": response.doc('#blogContent').html()
    }

在原先只保存 urltitle 的基础上,我们把文章相关的作者、发布时间、浏览次数、评论数、标签、摘要和正文都从页面上提取出来,具体的获取细节和上面类似,这里就不展开了。点击 run 可以看到提取的效果。

预览提取的数据

从预览来看,结果还是很符合我们的预期的。完成代码编辑和测试后,别忘了点右上角的 save,然后回到 Dashboard,将项目对的 status 改成 DEBUG 或者 RUNNING,然后点击 Run,爬虫就开始每天按计划进行爬取并更新数据啦。

任务完成后点击 Results 可以看到爬虫保存的数据,可以导出并做后续的加工处理。由于我们使用了 MariaDB 保存结果,实际上后续的数据处理过程也可以直接连接数据库获取数据。

总结

至此,我们的 PySpider 爬虫实战入门项目就完成了。华为开发者社区是一个开放的平台,因此数据获取也并没有受到太多阻碍,然而在实际的运用中,会遇到很多具有反爬虫策略的网站。道高一尺,魔高一丈,我们用上 ip 代理、模拟登陆、验证码识别、无头浏览器等等技术手段和网站维护人员斗智斗勇,还是有办法突破他们设置的封锁的。

这篇博文只是一个系列的开篇,后续我会为大家总结应对反爬虫策略的一些方法,以及数据分析和处理的技巧,欢迎大家一起交流。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,629评论 18 139
  • 爬虫文章 in 简书程序员专题: like:128-Python 爬取落网音乐 like:127-【图文详解】py...
    喜欢吃栗子阅读 21,742评论 4 412
  • 天桥手写: 人生旅途 走过的每一个地方,都是风景 写下的每一段话,都是故事 天桥手写经典,每日一篇经典语录,手写呈现。
    天桥手写阅读 301评论 2 0
  • 工作这么多年来,第一年让我觉得这些学生很可爱,不再为他们的调皮、任性而大发火,而是感谢他们带给我的很多促进、...
    sunflower80阅读 332评论 0 0
  • 我知道自己还是躲不过去了。 窗外基因局执法人员已经把这破烂不堪的小院团团围住。 基因局,2122年最有权力的部门。...
    ___南有嘉鱼___阅读 334评论 0 1