步骤:
1.目标网站:伯乐在线 http://www.jobbole.com/
爬取数据:文章
2.选择爬取策略
方式1 : 深度/广度优先算法爬取文章链接
- 分析url结构
- 点击 ‘全部文章’ 跳转子域名 : http://blog.jobbole.com/ 对该页面中的文章进行爬取,对每一篇文章的链接进行跟踪,爬取详情页面。
- 该子域名下面有 很多链接链接到其他二级域名或主域名下面的链接(比如首页--链接到主域名,资讯--链接到其他二级域名),这些域名需要被过滤掉。(后面具体讲解如何用scrapy过滤)
方式2:
该网站有可以获取到所有文章链接的入口 blog.jobbole.com/all-posts/ 则我们就不选择深度优先或广度优先的策略来爬取而首先考虑选择使用该入口,这样的抓取更有效率。
方法:
- 策略1: 分析url 页码变化与url的关系,提取出url的固定结构,则所有页的url就可以全部获取到。这样的话我们只需要往一个数组中传入 564 个数字,就可以把全部的数据获取到了,但是当页面总数发生变化时我们就需要去更改我们的源码,不够灵活,故不采用。
- 策略2:在该页面中提取 下一页 的链接,一直获取到最后一页,则所有的文章链接均可被提取出来了。
- 故我们在此选择策略2爬取文章链接
3.如何通过scrapy完成数据爬取
3.1.准备工作(创建虚拟环境+安装scrapy)
3.1.1.新建项目--创建虚拟环境
3.1.2.安装scrapy(因为在第一节中改变了虚拟环境存储路径在D:\Envs下,所以创建完成的虚拟环境存储在D盘,在该环境中安装)
出错:Command "d:\envs\articalspider\scripts\python.exe -u -c "import setuptools, tokenize;__file__='C:\\Users\\admin\\AppData\\Local\\Temp\\pip-install-atvfsvi6\\Twisted\\setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record C:\Users\admin\AppData\Local\Temp\pip-record-kx3gs00b\install-record.txt --single-version-externally-managed --compile --install-headers d:\envs\articalspider\include\site\python3.6\Twisted" failed with error code 1 in C:\Users\admin\AppData\Local\Temp\pip-install-atvfsvi6\Twisted\
解决方法:参考以下两篇博文解决:
https://blog.csdn.net/tanqu9315/article/details/79714249 + https://blog.csdn.net/qq_34733907/article/details/86591420
先安装对应版本的twisted
安装完成后,再次运行,安装scrapy成功
3.2.代码编辑
- 手动创建项目
D盘新建一个文件夹linuxshare
命令:scrapy startproject ArticalSpider
使用scrapy自带模板(scrapy可自定义模板,不涉及)
- pycharm中导入该工程
(scrapy大量借鉴了jango的涉及理念,所以我们可以看到大量的jango的影子,比如scrapy.cfg)
分析文件结构:
settings.py : 包含scrapy的相关配置
pipelines.py : 与数据存储相关的操作
middlewares.py : 可存放我们自己定义的middleware,自定义middleware可以让我们的scrapy变得更加可控
items.py : 定义数据保存的格式
spiders文件夹 : 存放具体网站的爬虫,没有给我们提供一个默认爬取网站的模板爬虫,需自行生成
生成网站模板文件方法:
scrapy提供了命令来生成模板文件
然后spiders文件夹中就会出现该爬虫文件
- 导入工程:
File-settings-搜索interpreter
- 在pycharm中实现scrapy的调试
先在爬虫文件中(jobbole.py)中打上断点,新建main文件,在main文件中执行脚本命令 scrapy crawl jobbole(jobbole来源于爬虫文件中的变量name='jobbole')调试,看response是否返回start_urls的值。
实现:
windows用户在虚拟环境中执行 scrapy crawl jobbole
出错:
pip install -i https://pypi.douban.com/simple pipiwin32
安装好之后再次运行即可启动爬虫jobbole成功。
将该命令配置到main文件中,在settings.py文件中更改ROBOTSTXT=False(如不更改,实际爬取时爬虫很快就会停止,后面的爬取都基于该参数的修改),然后调试main,代码会自动跳到断点处。
解析response
_url : 与start_urls 一致
response : HtmlResponse 还有TextResponse等,200代表服务器返回正常。
DEFAULT_ENCODING 和 encoding : 默认编码为ascii ,实际编码为 utf8
body : start_urls 的页面全文
- 如何通过xpath从html中提取出我们所需要的值?
scrapy 基于lxml提供了一套可以让我们通过xpath或者css选择器来提取数据的接口,接下来我们会通过xpath根据一个具体的url提取数据。
具体文章--测试这些值能否通过xpath获取,然后再进一步细化。
*xpath
- xpath简介
- xpath术语
- xpath语法
1.xpath使用路径表达式在xml和html中进行导航
xpath包含标准函数库
xpath是一个w3c的标准
2.html节点
3.xpath语法
表达式 | 说明 |
---|---|
article | 选取所有article元素的所有子节点 |
/article | 选取根元素article |
article/a | 选取所有属于article的子元素的a元素 |
//div | 选取所有的div元素(不论出现在文档的任何地方) |
article//div | 选取所有属于article元素的后代的div元素,不论它出现在article之下的任何位置 |
//@class | 选取所有名为class的属性 |
/article/div[1] | 选取属于article子元素的第一个div元素 |
/article/div[last()] | 选取属于article子元素的最后一个div元素 |
/article/div[last()-1] | 选取属于article子元素的倒数第二个div元素 |
//div[@lang] | 选取所有拥有lang属性的div元素 |
//div[@lang=‘eng’] | 选取所有拥有lang属性为eng的div元素 |
/div/* | 选取属于div元素的所有子节点 |
//* | 选取所有元素 |
//div[@*] | 选取所有带属性的title元素 |
/div/a | //div/p | 选取所有div元素的a和p元素 |
//span | //ul | 选取文档中的span和ul元素 |
article/div/p | //span | 选取所有属于article元素的div元素的p元素以及文档中所有的span元素 |
在网页中F12
将该xpath粘贴到代码中提取数据。
也可以自己根据代码提取更简洁的xpath(re2_selector)
*在此处执行时遇到了一个问题,我通过chrome浏览器提取出来的xpath是
//*[@id="post-114663"]/div[1]/h1/span
这样无法提取出title数据,需删掉span节点才能提取到数据
//*[@id="post-114663"]/div[1]/h1
接下来就可以提取文章内容了
*如何提高调试效率 :在命令行中使用shell调试
scrapy shell url
提取时间
提取赞数
提取收藏数和评论数(正则表达式,用之前测试正则表达式的python文件测试一下正则表达式是否正确)
获取页面内容(html内容暂不做处理)
response.xpath("//div[@class='entry']/text()").extract()
提取文章tag
最后将调试过的命令写到爬虫文件中
- 如果对前端熟悉的同学可已使用CSS选择器提取相应内容,在此不多做介绍。
然后在pycharm中调试一下,看结果是否正确。
问题:
评论数、点赞数、收藏数为0时,无法抓取数字该怎么办?
解决方法: