你所需要的,不仅仅是一个好用的代理。
scrapy 是一个用 python 语言编写的,为了爬取网站数据,提取结构性数据而编写的应用框架。
本文使用的环境:
python 3.5.2
pip 9.0.1
操作系统: Ubuntu 16.04
在官网下载 Ubuntu 环境下的python3.5的安装包,安装,安装完成后,检查一下 python 的安装情况,一般pyhton安装的时候,pip 也是一起安装好的,如果没有安装完全,再将 pip 也一起安装好。
现在Ubuntu默认是安装 python2.7 的,避免两个环境之间切换的麻烦,我们安装 python 虚拟环境来解决这个问题。
pip install virtualenv
pip install virtualwrapper
pip list # 查看已安装
virtualenv 能够通过根据不同的 python 版本创建对应不同版本的虚拟环境,virtualwrapper 能够方便的在不同的虚拟环境之间进行切换。安装完成之后,下面我们创建一个 python3.5.2 版本的虚拟环境
source /usr/local/bin/virtualwrapper.sh #这个与 windows 不一样,需要先执行一下脚本才能生效,大家可以打开这个文件看一下
# 创建一个名为 py3Scrapy 的虚拟环境
mkvirtualenv py3Scrapy -p /usr/bin/python3.5
# workon 查看创建好的虚拟环境,虚拟环境的保存路径可以通过 `VIRTUALENV_PYTHON` 来配置
workon
workon py3Scrapy # 进入选择的虚拟环境
如下图所示:
python的版本也能查看得到,进入虚拟环境之后,在shell前面会出现虚拟环境的名称,退出虚拟环境
deactivate
好了,创建好环境之后,现在来开始我们的 scrapy 之旅吧。
scrapy 是基于 twisted 框架的,大家会发现,安装 scrapy 的时候,会需要安装很多包。
pip install scrapy
使用 pip 进行安装,方便,但是这种默认的安装方式,实在官网下载安装包来进行安装的,比较慢,大家可以 使用豆瓣源来进行安装
pip install scrapy -i https://pypi.douban.com/simple
这种方式,下载会非常的快,安装其他的包都可以使用这种方法,但是,如果使用豆瓣源安装的时候,提示找不到符合版本的安装包,那就使用第一种办法进行下载安装,因为豆瓣源可能没有官网那么及早更新。
因为每个人的环境都可能存在差异,安装过程中会出现一些问题。当如果报错,twisted 安装失败的时候,建议从官网下载 twisted 安装包,自行进行安装,安装完成之后,再接着继续上面 scrapy 的安装,安装完成之后,检查一些安装结果
scrapy -h
好了,环境准备好之后,接下来我们来分析一下伯乐在线的文章网页结构
伯乐在线网站不需要我们先登录,然后才能访问其中的内容,所以不需要先模拟登录,直接就能访问网页。伯乐在线地址为 https://www.jobbole.com
,这上面的文章质量还是不错的,大家有时间可以看看。
我们随便找一篇文章试图来分析一下,比如 http://blog.jobbole.com/111469/
,F12进入浏览器调试窗口,从全文分析,比如我们想获取文章标题,文章内容,文章创建时间,点赞数,评论数,收藏数,文章所属类别标签,文章出处等信息
打开文章链接,我们获取到的是一个html页面,那么如何获取上面所说的那些数据呢,本文通过 CSS 选择器来获取(不了解 CSS selector的小伙伴可以先去熟悉一下 http://www.w3school.com.cn/cssref/css_selectors.asp
)。 scrape 为我们提供了一个 shell 的环境,可以方便我们进行调试和实验,验证我们的css 表达式能够成功获取所需要的值。下面启动 scrapy shell
scrapy shell "http://blog.jobbole.com/111469/"
scrapy 将会帮助我们将 http://blog.jobbole.com/111469/
这个链接的数据捕获,现在来获取一下文章标题,在浏览器中找到文章标题, inspect element
审查元素,如下图所示:
文章标题为 王垠:如何掌握所有的程序语言 ,从上图获知,这个位于一个 class 名为 entry-header
的 div 标签下的子标签 h1 中,那我们在 scrapy shell 通过 css 选择器来获取一下,如下图所示:
仔细查看上图,注意一些细节。通过 response.css 方法,返回的结果是一个 selector,不是字符串,在这个 selector 的基础上可以继续使用 css 选择器。通过 extract() 函数获取提取的标题内容,返回结果是一个 list,注意,这里是一个 list ,仍然不是字符串 str,使用 extract()[0] 返回列表中的第一个元素,即我们需要的标题。
但是,如果标题没有获取到,或者选择器返回的结果为空的话,使用 extract()[0] 就会出错,因为试图对一个空链表进行访问,这里使用 extract_first() 方法更加合适,可是使用一个默认值,当返回结果为空的时候,返回这个默认值
extract_first("") # 默认值为 ""
此处仅仅是将 title 标题作为一个例子进行说明,其他的就不详细进行解释了,主要代码如下所示:
title = response.css(".entry-header h1::text").extract()[0]
match_date = re.match("([0-9/]*).*",
response.css(".entry-meta-hide-on-mobile::text").extract()[0].strip())
if match_date:
create_date = match_date.group(1)
votes_css = response.css(".vote-post-up h10::text").extract_first()
if votes_css:
vote_nums = int(votes_css)
else:
vote_nums = 0
ma_fav_css = re.match(".*?(\d+).*",
response.css(".bookmark-btn::text").extract_first())
if ma_fav_css:
fav_nums = int(ma_fav_css.group(1))
else:
fav_nums = 0
ma_comments_css = re.match(".*?(\d+).*",
response.css("a[href='#article-comment'] span::text").extract_first())
if ma_comments_css:
comment_nums = int(ma_comments_css.group(1))
else:
comment_nums = 0
tag_lists_css = response.css(".entry-meta-hide-on-mobile a::text").extract()
tag_lists_css = [ele for ele in tag_lists_css if not ele.strip().endswith('评论')]
tags = ','.join(tag_lists_css)
content = response.css(".entry *::text").extract()
解释一下 create_date
,通过获取到的值,存在其他非时间的数据,通过 re.match 使用正则表达式来提取时间。
好了,所有需要的值都提取成功后,下面通过 scrapy 框架来创建我们的爬虫项目。
开始我们的爬虫项目
scrapy startproject ArticleSpider
scrapy 会为我们创建一个名为 ArticleSpider 的项目
进入到 ArticleSpider 目录,使用basic模板创建
scrapy genspider jobbole blog.jobbole.com
创建完成之后,我们使用 pycharm 这个IDE打开我们创建的爬虫项目,目录结构如下所示:
我们可以在 items.py 里面定义数据保存的格式,在 middlewares.py 定义中间件,在 piplines.py 里面处理数据,保存到文件或者数据库中等。在 jobbole.py 中对爬取的页面进行解析。
下面,我们首先需要做的,就是利用我们编写的 css 表达式,获取我们提取的文章的值。在 jobbole.py 中,我们看到
class JobboleSpider(scrapy.Spider):
name = 'jobbole'
allowed_domains = ['blog.jobbole.com']
start_urls = ['http://blog.jobbole.com/all-posts/']
def parse(self, response):
pass
scrapy 为我们创建了一个 JobboleSpider 的类,name 是爬虫项目的名称,同时定义了域名以及爬取的入口链接。scrapy 初始化的时候,会初始化 start_urls
入口链接列表,然后通过 start_requests
返回 Request 对象进行下载,调用 parse 回调函数对页面进行解析,提取需要的值,返回 item。
所以,我们需要做的,就是将我们在上一小节编写的代码放在 parse 函数中,同时,将 start_urls 的值,改为上面我们在 scrapy shell 爬取的页面的地址 http://blog.jobbole.com/111469/
,因为我们这里还没有讲到通过 item 获取我们提取的值,此处你可以通过 print() 函数将值进行打印。在 shell 中启动爬虫(先进入我们的工程目录)
scrapy crawl jobbole
既然我们使用了 pycharm 这个IDE,那么我们就不用 shell 来启动爬虫,在 ArticleSpider 目录下创建一个 main.py 文件
from scrapy.cmdline
import execute
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
execute(["scrapy", "crawl", "jobbole"])
上面的代码,就是将当前项目路径加入到 path 中,然后通过调用scrapy 命令行来启动我们的工程。然后,通过设置断点调试,一步一步查看我们的提取的变量的值是否正确。
注意:启动之前,将 settings.py 中的 ROBOTSTXT_OBEY
这个参数设置为 False
这样,我们就爬取到了伯乐在线的这一篇文章了。
既然我们已经能够获取到某一篇文章的数据,那么下面就来获取所有文章的链接。
伯乐在线所有文章链接的入口地址为 http://blog.jobbole.com/all-posts/
,通过浏览器进入调试模式查看文章列表的链接,如下图所示
文章链接是在 id 为 archive 的 div 标签下的子 div 标签之下, class 为 post-thumb,这个下面的子标签 a 的 href 属性,仍使用上面说的 scrapy shell 的方法,如下图所示
可以看出,获得了当前页面所有的文章的 url,这仅仅是当前页面的所有 url,我们还需要获取下一页的 url,然后通过下一页的 url 进入到下一页,获取下一页的所有文章的 url,依次类推,知道爬取完所有的文章 url。
在文章列表的最后,有翻页,分析如下
下一页是 class 为 next page-numbers 的 a 标签中,如下图