声明:本文内容和涉及到的代码仅限于我的学习,任何人不得做为商业用途。转载请附上此文章地址php
本篇文章Python初学者之网络爬虫的继续,最新代码已提交到https://github.com/octans/PythonPracticehtml
上篇文章Python初学者之网络爬虫中我从花椒的热门推荐页面入手,进而获取到主播我的信息和对应的直播历史视频。python
首先看一下上一篇文章中对huajiao.com的主播和视频的爬取成果:mysql
# getUserCount 10179 # getLiveCount 111574
到目前为止我新作了以下事情:git
其中对MySql的封装代码单独放到了文件mysql.py下,作为一个module使用,这个module虽然简单,但已经实现了select,insert,delete等操做,对MySql封装感兴趣的同窗能够参考, 但请不要用于生产环境。推荐去使用和阅读数据库类peewee。
接下来将继续讲述我在数据抓取上的开发经历。github
最终目标:收集到各大直播平台的主播信息和历史播放记录,进而对数据进行聚合分析。
当前已完成:对花椒网的数据收集。
沃米优选网(http://video.51wom.com/)是一个网红数据聚合的网站,它收集了各个直播平台(花椒,熊猫,秒拍,斗鱼,映客,一直播,美拍)的热门主播信息。因此我但愿能从它这里获取到各个平台的热门主播信息,以后拿着主播id去对应的直播平台去爬取更详细的信息。ajax
列表页http://video.51wom.com/截图以下:
初看这是一个列表页,而且底部有分页连接,点击分页时触发表单提交sql
当点击底部分页时,使用chrom开发者工具,看到有XHR请求以下截图:数据库
从截图和一些测试能够分析出:json
对于cookie容易拿到,但_csrf如何获取呢?
查看页面源码,发现网站在生成列表页时已经将csrf的值写入了表单;同一个csrf值在后续请求中能够屡次使用
<input type="hidden" name="_csrf" value="aWF6ZGMzclc9EAwRK3Y4LhobNQo6eEAdWwA0IFd1ByUDNTgwClUEZw==">
由以上分析,程序的逻辑应该这样,
a) 构造基础类class Website, 以后为每一个网站创建一个class,继承Website
注意如下代码为了节省篇幅,不是完整代码,也非PEP8代码规范
class Website: ### 使用requests.session()可以自动处理cookies session = requests.session() ### 设置html解析器 htmlParser = BeautifulSoup ### 设置json解析器 jsonParser = json ### 设置headers headers = { 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/' '54.0.2840.98 Safari/537.36' } ### 直接发起get请求 def get(self, url, params=None): if params is None: params = {} return self.session.get(url, params=params, headers=self.headers) ### 发起get请求并返回解析后的html对象 def get_html(self, url, params=None): r = self.get(url, params) return self.htmlParser(r.text, 'html.parser') ### 发起get请求并返回解析后的json对象 def get_json(self, url, params=None): r = self.get(url, params) return self.jsonParser.loads(r.text) ### 发起post请求,以Content-Type:multipart/form-data方式 def post_multi_part(self, url, params): kwargs = dict() for (k, v) in params.items(): kwargs.setdefault(k, (None, v)) r = self.session.post(url, files=kwargs, headers=self.headers) return self.htmlParser(r.text, "html.parser")
b) 构造class WoMiYouXuan, 封装对网站沃米优选的请求
class WoMiYouXuan(Website): ### 发起post请求时须要将csrf发给网站 csrf = '' def __init__(self): self.first_kiss() ### 首次访问该网站获取到csrf值并保存到self.csrf, 供其余post请求直接使用 def first_kiss(self): url = 'http://video.51wom.com/' html = self.get_html(url) self.csrf = html.find('meta', {'name': 'csrf-token'}).attrs['content'] ### 从主播列表页获取主播信息 def parse_actor_list_page(self, page=1): ### 构造参数->发起post请求 url = 'http://video.51wom.com/media/' + str(page) + '.html' keys = ('_csrf', 'stage-name', 'platform', ' industry', 'price', 'follower_num', 'follower_area', 'page', 'is_video_platform', 'sort_by_price', 'type_by_price') params = dict() for key in keys: params.setdefault(key, '') params['_csrf'] = self.csrf params['page'] = str(page) html = self.post_multi_part(url, params) ### 解析主播列表 trs = html.find('div', {'id': 'table-list'}).table.findAll('tr') trs.pop(0) # 去除标题行 actor_list = list() for tr in trs: ### 后面太多了,有兴趣的同窗去看源码吧 ### 骨架函数,循环访问每一个分页数据并将结果写入mysql def spider_actors(self): page = 1 tbl_actor = WMYXActor() while True: ret = self.parse_actor_list_page(page) for actor in ret['items']: actor['price_dict'] = json.dumps(actor['price_dict']) tbl_actor.insert(actor, replace=True) if ret['items_count'] * ret['page'] < ret['total']: page += 1 else: break
方法parse_actor_list_page()具体分析主播列表的html代码,这是一个细致活;感觉一下代码截图
a) 表单提交的POST方式
一般只提交一些kv数据时,使用application/x-www-form-urlencoded方式;
一般上传文件时,使用multipart/form-data方式,但此种方式也是能够提交kv类数据的,好比上面的获取主播列表数据时就是使用此方式。
b) Python的网络请求库Requests
这个库太好用了!而且可以对cookie自动处理,好比我在基类Website中的使用方式; 而且使用它构造multipart/form-data方式的post请求也很方便,好比方法Website::post_multi_part()
c) Python中使用正则匹配字符串中的整数,以下代码:
avg_watched = tds[6].get_text(strip=True) # 平均观看人数 mode = re.compile(r'\d+') tmp = mode.findall(avg_watched)
d) 使用try, except机制来实现相似php里的isset(),以下代码:
# 判断是否有逗号,好比8,189 try: index = string.index(',') string = string.replace(',', '') except ValueError: string = string
e) 必定要注意python中的’1’和1是不同的,须要你本身来作字符串和数字的类型转换
在沃米优选网拿到了各个直播平台的主播id, 先实现对一下网(http://www.yixia.com/)的抓取,获取对应的主播和视频信息。
一下网的我的主页地址为http://www.yixia.com/u/uid, 这个uid就是主播id, 以下截图:
class YiXia(Website): ### 访问主播页面,也是视频列表页,从该页面获取到suid和主播我的信息 def parse_user_page(self, uid): print(self.__class__.__name__ + ':parse_user_page, uid=' + uid) user = dict() user['uid'] = uid url = 'http://www.yixia.com/u/' + uid bs = self.get_html(url) div = bs.find('div', {'class': 'box1'}) user['nickname'] = div.h1.a.get_text(strip=True) # 昵称 stat = div.ol.get_text(strip=True) stat = re.split('关注\||粉丝', stat) user['follow'] = stat[0].strip() # 关注数 user['followed'] = stat[1].strip() # 粉丝数 ### ------这里省略不少代码---- return user ### AJAX请求视频列表 def get_video_list(self, suid, page=1): url = 'http://www.yixia.com/gu/u' payload = { 'page': page, 'suid': suid, 'fen_type': 'channel' } json_obj = self.get_json(url, params=payload) msg = json_obj['msg'] msg = BeautifulSoup(msg, 'html.parser') ### 解析视频标题 titles = list() ps = msg.findAll('p') for p in ps: titles.append(p.get_text(strip=True)) # 视频标题 ### 解析视频赞和评论数 stats = list() divs = msg.findAll('div', {'class': 'list clearfix'}) for div in divs: tmp = div.ol.get_text(strip=True) tmp = re.split('赞|\|评论', tmp) stats.append(tmp) ### 解析视频其余数据 videos = list() divs = msg.findAll('div', {'class': 'D_video'}) for (k, div) in enumerate(divs): video = dict() video['scid'] = div.attrs['data-scid'] ### ------这里省略不少代码------ return videos ### 骨架函数,获取每一个视频的每一个分页数据 def spider_videos(self, suid, video_count): page = 1 current = 0 tbl_video = YiXiaVideo() while current < int(video_count): print('spider_videos: suid=' + suid + ', page=' + str(page)) videos = self.get_video_list(suid, page) for video in videos: tbl_video.insert(video, replace=True) current += len(videos) page += 1 return True
大部分仍是3.3里的知识点,这里重点注意字符串和整形,浮点型数字的转换。好比粉丝数’2.3万’是一个字符串,须要转成浮点数2.3或者整数23000;再好比’8,189’须要转成8189.
如下截图为采集到的一下网视频数据:
这里列出我记录下来的参考连接: