一次性付费进群,长期免费索取教程,没有付费教程。
进微信群回复公众号:微信群;QQ群:460500587
教程列表 见微信公众号底部菜单 | 本文底部有推荐书籍微信公众号:计算机与网络安全
ID:Computer-network
编写一个完整的“纯粹的”Pyspider项目。选一个简单的项目,在电影下载网站爬取所有的欧美影片的下载地址。
1、项目分析
选择的电影下载网站是http://www.ygdy8.com。在浏览器中打开这个网站,在网页顶部使用鼠标单击欧美电影链接,如图1所示。
图1 项目起始页
此时显示项目的起始页面为http://www.ygdy8.com/html/gndy/oumei/index.html,移动页面右侧的下拉条,查看页面底部。将鼠标移动到末页链接上,页面左下角将显示该链接的链接地址,如图2所示。
图2 项目末页
单击末页链接,进入末页页面。在页面底部将鼠标分别移动到各个链接上,以分析链接地址规则,如图3所示。
图3 分析链接地址
单击首页链接,与开始的页面http://www.ygdy8.com/html/gndy/oumei/index.html对比一下,两者完全相同,因此可以得出结论:这个项目需要爬取的链接为http://www.ygdy8.com/html/gndy/oumei/list_7_,加上[1.html, 189.html]。
在这些链接中需要得到两个数据,一个是电影的名字,一个是电影的链接。以最后一页的最后一个电影为例,将鼠标移动到最后一个电影的链接上,如图4所示。
图4 有效数据
项目的基本流程就是这样了,首先分析这189个页面,获取电影名字和电影链接,然后到链接页面获取电影的具体信息,比如放映时间、电影主演、下载地址等。
2、爬虫编写
在浏览器中打开Pyspider的webui(Pyspider控制台),单击Create按钮创建新项目。项目名为oumeiMovie,起始地址可以为空。单击Create按钮进入调试界面。
与上一个项目youkuHotTvList不同,这个项目需要爬去的页面比较多,目前是189页。在函数on_start回调index_page函数之前,必须先把所有需要爬的页面统计出来,因此在Handler类中添加一个构造函数,用来统计爬取的页面,再到on_start函数中,循环回调index_page函数处理这些页面,如图5所示。
图5 统计爬取页数
单击页面右侧代码编辑区右上角的save按钮后,单击页面左侧预览区右上角的Run按钮,执行结果如图6所示。
图6 爬虫入口页面
显示有189页,与设计结果相符。url显示也是正常的。继续下一步,修改index_page函数。在这个函数中获取电影名称和电影下载的页面,然后通过电影下载页面链接回调detail_page函数获取最终的数据。打开页面源码,找到离有效数据最近的标签。这里离有效数据最近,也最容易识别的是Table标签,如图7所示。
图7 页面源码中定位有效数据标签
查找显示有25个相同的标签,这与页面显示是相符合的。根据查找得到的标签,修改index_page函数,如图8所示。
图8 修改index_page函数
在index_page函数中并没有使用pyquery定位过滤,而是使用的bs4定位过滤。这是因为所需数据movieName存在于标签内,而在标签附近有一个与它属性完全相同的标签。如果使用pyquery来定位,要么用next方法来分辨,要么添加条件判断来分辨,不如bs4方便。现在来测试一下结果,单击Run按钮,选择页面预览栏下面任意一个链接,单击右侧的三角运行按钮,如图9所示。
图9 获取页面下载页
从中可以看出,index_page函数解析出了25个结果。这与页面源码中搜索得到的结果是相符的。爬虫程序的下一步是使用回调detail_page函数,用于处理得到的下载页面链接地址。
注意,这个回调函数self.crawl(url, callback=self.detail_page, save={‘movieName’:movieName})与Pyspider默认的回调函数略有不同,这里多出了一个save的字典参数。这个参数很重要,有时候index_page函数需要将一些参数传递给detail_page函数(在这个爬虫中也可以不要,因为在下一步detail_page函数中也可以解析得到movieName,这里只是做演示)。但两个函数都是回调函数,是无法直接传递参数的。使用全局变量传递参数也不行,因为Pyspider采用的是广度优先算法(深度优先算法倒是可以用全局变量传参),全局变量只能传递最后一个页面的参数。因此这里不得不借助其他的参数来传递值。再来仔细看一下回调的函数detail_page,它定义的是def detail_page(self, response),只带有一个参数response。这就意味着如果index_page函数需要传参给detail_page函数,就只能借助response这个参数来传递了。response这个参数是怎么样的呢?它来自于Pyspider的安装目录下的lib/response.py,到该目录下执行命令:
vi response.py
执行结果如图10所示。
图10 使用save参数传值
这里save定义的是None,其实可以将save定义为任何类型。也许会有多个值需要传递,还是将save定义为字典更方便。
回到爬虫程序,继续下一步。利用save传递参数,修改detail_page函数,获取最终需要的数据。任意挑选一个电影下载页面的链接在浏览器中打开。
查看该页面的源代码,找到这些有效数据的位置,如图11所示。
图11 源码中有效数据
电影下载链接的定位和过滤不难,不管是用bs4还是pyquery都很简单,但电影信息就有点麻烦了。因为源代码中电影信息内使用了大量的
标签。众所周知,
标签是由
…标签组“进化”而来的,但bs4和pyquery都只能从闭合的标签组中过滤出有效数据(至少目前是如此)。所以在这里要过滤出电影的信息,就要先把
标签过滤出去,然后在剩下的数据中使用re模块来过滤有效信息。因此,修改爬虫中函数detail_page,如图12所示。
图12 过滤电影有效信息
先单击爬虫页面右侧代码编辑区右上角的Save按钮保存爬虫代码,再单击页面左侧页面预览区右上角的Run按钮,在左侧页面预览区下部过滤得出最终所需的有效数据。
3、爬虫运行、调试
现在这个爬虫基本上编辑完毕了,可以试运行一下。单击页面左上角的pyspider链接,或者在地址栏中输入Pypider爬虫IP:5000,进入Pyspider主界面(控制台)。
每个爬虫都被分为了7列,分别为Group、Project Name、Status、rate/burst、avg time、Progress、Actions。
Group:组名是可以修改的,直接在组名上单击进行修改。如果需要对爬虫进行标记,可以通过修改组名来完成。
组名改为delete后如果状态为stop状态,24小时后项目会被系统删除。
Project Name:项目名只能在开始创建时确定,不可修改。
Status:显示的是当前项目的运行状态。每个项目的运行状态都是单独设置的。直接在每个项目的运行状态上单击进行修改。运行分为五个状态:TODO、STOP、CHECKING、DEBUG、RUNNING。
各状态说明:TODO是新建项目后的默认状态,不会运行项目;STOP状态是停止状态,也不会运行;CHECKING是修改项目代码后自动变的状态;DEBUG是调试模式,遇到错误信息会停止继续运行;RUNNING是运行状态,遇到错误会自动尝试,如果还是错误会跳过错误的任务继续运行。
Rate/Burst:这一列是速度控制。Rate是每秒爬取的页面数,Burst是并发数。默认情况下是1/3,意思是每秒3个并发,每个并发爬一个页面。这一项是可以调整的,如果被爬的网站没做什么限制,可以把这个数稍微调高一点。
Avg Time:平均运行时间。
Progress:爬虫进展统计。一个简单的运行状态统计。5m是五分钟内任务执行情况,1h是一小时内运行任务统计,1d是一天内运行统计,all是所有的任务统计。
Actions:这一列包含了3个按钮,即Run、Active Tasks、Results。run按钮是项目初次运行需要点的按钮,这个功能会运行项目的on_start方法来生成入口任务。Active Tasks按钮显示最新任务列表,方便查看状态,查看错误。Results按钮查看项目爬取的结果。
目前爬虫刚编辑完成,所以该项目状态为TODO,单击TODO状态,在弹出的菜单中将状态修改为DEBUG(也可以选择RUNNING),如图13所示。
图13 修改项目运行状态
默认的速度是每秒3次,这个速度太慢了。单击项目的Rate/Burst列,修改并发数和爬取页面的速度,暂时修改为3/10,如图14所示。
图14 爬虫速度控制
设置完毕后,单击项目右侧的Run按钮,开始运行爬虫。稍等几分钟后就会有结果出现了。单击项目右侧的Results按钮,如图15所示。
图15 爬虫结果
在页面右上角选择保存的格式,可以保存为json、txt、csv三种格式(基本上是够用的)。回到Pyspider控制台,单击项目右侧的Active Tasks按钮,如图16所示。
图16 爬虫Log
显示有2次错误。单击错误链接查看错误原因,一般都是因为bs4按照过滤条件没有获取到合适的结果造成的,按照提示修改爬虫代码。最终修改后的爬虫代码如下:
1 #!/usr/bin/env python
2 # -*- encoding: utf-8 -*-
3 # Created on 2017-12-15 14:03:00
4 # Project: oumeiMovie
5
6 from pyspider.libs.base_handler import *
7 from bs4 import BeautifulSoup
8 import re
9 import codecs
10
11
12 class Handler(BaseHandler):
13 crawl_config = {
14 }
15
16 def __init__(self):
17 self.urls = []
18 for page in range(1, 178 + 1):
19 url = 'http://www.ygdy8.com/html/gndy/oumei/list_7_' +str(page) + '.html'
20 self.urls.append(url)
21
22 @every(minutes=24 * 60)
23 def on_start(self):
24 for url in self.urls:
25 self.crawl(url, callback=self.index_page)
26
27 @config(age=10 * 24 * 60 * 60)
28 def index_page(self, response):
29 soup = BeautifulSoup(response.text, 'lxml')
30 try:
31 Tags = soup.find_all('table', attrs={'width':'100%','border':'0','cellspacing':'0','class':'tbspan','style':'margin-top:6px'})
32 except Exception as e:
33 pass
34 for tag in Tags:
35 try:
36 movieName = tag.find_all('a')[-1].get_text()
37 movieHref = tag.find_all('a')[-1].get('href')
38 url = 'http://www.ygdy8.com' + movieHref
39 except Exception as e:
40 pass
41 self.crawl(url, callback=self.detail_page, save={'movieName':movieName})
42
43
44 @config(priority=2)
45 def detail_page(self, response):
46 movieName = response.save.get('movieName')
47 html = re.sub('
{1,5}', '', response.text)
48 try:
49 dataStr = re.search(u'◎.*
50 except Exception as e:
51 pass
52 soup = BeautifulSoup(response.text, 'lxml')
53 try:
54 hrefTag = soup.find('td', attrs={'style':'WORD-WRAP: break-
word', 'bgcolor': '#fdfddf'})
55 href = hrefTag.find('a').get('href')
56 except Exception as e:
57 pass
58 dataList = dataStr.split(u'◎')
59 dataDic = {u'译名':'', u'片名':'' , u'年代':'',u'产地':'',u'类别':'',\
60 u'语言':'' , u'字幕':'' , u'IMDb评分 ':'' , u'豆瓣评分':'' , u'文件格式':'' , u'downUrl ': href,\
61 u'视频尺寸':'' , u'文件大小':'' , u'片长':'' , u'导演':'' ,u'主演':'' , u'简介 ':'', u'movieName':movieName}
62 for key in dataDic.keys():
63 for st in dataList:
64 if re.search(key, st):
65 dataDic[key] = re.sub(key, '', st)
66 break
67 return dataDic
在中文字符前加上u,意思是指字符编码为unicode。
解决方法起始很简单,只要在bs4搜索结果时加上except就可以了。好了,现在已经将爬虫代码修改完毕,保存后回到Pyspider控制台。单击oumeiMovie项目中的Run按钮,观察Active Tasks的结果,却发现Pyspider爬虫虽然从入口启动,但并没有真正地开始作业。这是因为Pyspider爬虫除了第一次运行时是用单击Run按钮启动的,之后爬虫的运行是由代码中的scheduler(调度器)控制的,如图17所示。
图17 Pyspider scheduler
从图17中可以看到,在Pyspider默认定义的三个函数的上一行中有一个以@开头的函数。这个是Python的装饰器。装饰器是Python的高阶函数这里不做解释。有兴趣的朋友可自行上网搜索学习。@every(minutes=24*60)在这里是作为调度器使用的,意思是on_start函数每天执行一次。@config(age=10*24*60*60)也是调度器,意思是当前request的有效期是10天,10天内遇到相同的请求将忽略。最后@config(priority=2)是优先级设置,数字越大就越先执行。没有特殊要求,一般不需要修改默认的设置。
4、删除项目
项目运行完毕,得到了想要的结果。保存好结果,这个项目就没什么用处了。下一步就是删除项目了。删除Pyspider项目的方法有两种。
方法一:官方推荐常规的删除方法是将需要删除的项目状态设置为STOP,然后把项目的Group修改为delete,如图18所示。
图18 删除项目常规方法
在24小时后,Pyspider的服务端就将该项目删除了。这是为了给性急的用户一个后悔的机会,如果认定不后悔,那就参考方法二。
方法二:Pyspider所有的文档都保存在启动Pyspider的目录下的data文件夹中,而所有的项目都保存在data文件夹下的project.db文档内,因此删除Pyspider项目,只需要在project.db文档中删除相应的记录就可以了。执行命令:
ls
cd data
ls
sqlite3 project.db
.tables
select name from projectdb;
delete from projectdb where name='test';
如果没有安装Sqlite3,则需要使用apt-get安装。
执行结果如图19所示。
图19 从文档中删除项目
回到Pyspider控制台页面,刷新一下,可以看到test项目已经被删除了,如图20所示。
图20 快速删除Pyspider项目
同理,如果需要修改项目的result、task,只需要用Sqlite3命令修改相应的数据库文件就可以了。
微信公众号:计算机与网络安全
ID:Computer-network
【推荐书籍】