专栏名称: Python开发者
人生苦短,我用 Python。伯乐在线旗下账号「Python开发者」分享 Python 相关的技术文章、工具资源、精选课程、热点资讯等。
目录
相关文章推荐
Python爱好者社区  ·  模仿一下领导说话的样子 ·  15 小时前  
Python爱好者社区  ·  今年程序员这薪资是认真的吗? ·  昨天  
Python开发者  ·  DeepSeek+软考,杀疯了?! ·  3 天前  
Python爱好者社区  ·  机器学习全书 ·  3 天前  
Python爱好者社区  ·  梁文锋导师项志宇引关注,个人主页满满都是对团 ... ·  3 天前  
51好读  ›  专栏  ›  Python开发者

Python 初学者之网络爬虫(2)

Python开发者  · 公众号  · Python  · 2016-12-22 21:40

正文

(点击 上方公众号 ,可快速关注)


来源:伯乐在线专栏作者 - octans

欢迎投稿,请点击这里查看详情;

如需转载,发送「转载」二字查看说明


本篇是文章 Python初学者之网络爬虫 的后续,最新代码已提交到https://github.com/octans/PythonPractice


1. 上篇回顾


上篇文章Python初学者之网络爬虫中,我从花椒的热门推荐页面入手,进而获取到主播个人信息和对应的直播历史视频。


首先看一下上一篇文章中对huajiao.com的主播和视频的爬取成果:


# getUserCount

10179

# getLiveCount

111574


到目前已收集了10179个主播信息,和这些主播的111574个视频信息。这里数据量小的原因是我只收集了花椒热门推荐下面的主播,这个页面每次展示60个系统推荐的主播。


到目前为止我新做了如下事情:


  • 对MySql的读写操作进行了封装

  • 编码风格遵从PEP8

  • 爬取沃米优选网(http://video.51wom.com/)的主播信息

  • 爬取一下网(http://www.yixia.com/)的主播信息和视频信息


其中对MySql的封装代码单独放到了文件mysql.py下,做为一个module使用,这个module虽然简单,但已经实现了select,insert,delete等操作,对MySql封装感兴趣的同学可以参考, 但请不要用于生产环境。推荐去使用和阅读数据库类peewee。


接下来将继续讲述我在数据抓取上的开发经历。


2. 爬取的数据源和逻辑


最终目标:收集到各大直播平台的主播信息和历史播放记录,进而对数据进行聚合分析。


当前已完成:对花椒网的数据收集。


沃米优选网(http://video.51wom.com/)是一个网红数据聚合的网站,它收集了各个直播平台(花椒,熊猫,秒拍,斗鱼,映客,一直播,美拍)的热门主播信息。所以我希望能从它这里获取到各个平台的热门主播信息,之后拿着主播id去对应的直播平台去爬取更详细的信息。


3. 爬取沃米优选网的主播列表页


列表页http://video.51wom.com/截图如下:



初看这是一个列表页,并且底部有分页链接,点击分页时触发表单提交


3.1 分析结论和构思程序逻辑


当点击底部分页时,使用chrom开发者工具,看到有XHR请求如下截图:



从截图和一些测试可以分析出:


  • a) 要请求第二页以后的数据,需要将相应的cookie和csrf数据提交给网站;

  • b) 提交的方式是POST的”multipart/form-data”;

  • c) 提交的参数有_csrf, stage-name, platform, industry等;

  • d) 请求的返回结果是一个表格列表的html代码;


对于cookie容易拿到,但_csrf如何获取呢?


查看页面源码,发现网站在生成列表页时已经将csrf的值写入了表单;同一个csrf值在后续请求中可以多次使用;



由以上分析,程序的逻辑应该这样,


  • a) 先请求主播列表的首页,获取到csrf值和cookie

  • b) 将csrf和cookie值保存,用于下次请求

  • c) 请求主播列表的第二页,第三页等

  • d) 将获取到的表格列表的html代码使用BeautifulSoup进行解析,遍历每个行,行里的每个列

  • e) 将获取到的数据写入mysql


3.2 python编码获取沃米优选网的主播信息


a) 构造基础类class Website, 之后为每个网站建立一个class,继承Website


  • 有些请求返回的是html代码,类里设置好html解析器;

  • 有些请求返回的是json串,基类里设置好json的解析器;

  • 请求每个网站时,需要设置不同的header,将header放在基类;

  • 对post的Content-Type:multipart/form-data方式进行函数封装;

  • 对post的Content-Type: application/x-www-form-urlencoded方式分别进行函数封装;

  • 这里面尽量把各种不同的请求方式写成函数,而不使用type参数的形式,便于子类清晰的调用;


注意以下代码为了节省篇幅,不是完整代码,也非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, 封装对网站沃米优选的请求


  • 方法first_kiss()用于第一次请求网站,获取到csrf值由属性self.csrf保存;

  • first_kiss()另一个作用是获取到cookie,虽然没有显示处理,因为requests.session()帮我们处理了,自动获取自动提交;

  • 注意在一个实例里,只需调用一次first_kiss()即可,之后就可以多次调用其他的页面请求函数了;

  • csrf和cookie是由关联的,网站会校验,都要提交;

  • 方法parse_actor_list_page()是具体分析主播的列表html代码,这是一个细致活;

  • 方法spider_actors是骨架函数,循环访问每个分页数据并将结果写入mysql;


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代码,这是一个细致活;感受一下代码截图



3.3 知识点总结


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是不一样的,需要你自己来做字符串和数字的类型转换


4. 爬取秒拍网的主播和视频信息


在沃米优选网拿到了各个直播平台的主播id, 先实现对一下网(http://www.yixia.com/)的抓取,获取对应的主播和视频信息。


一下网的个人主页地址为http://www.yixia.com/u/uid, 这个uid就是主播id, 如下截图:



4.1 分析结论和构思程序逻辑


  • a) 在主播个人主页能够拿到主播的个人信息,如头像,昵称,粉丝数等,还能拿到主播的视频列表;

  • b) 视频列表的加载方式是瀑布流方式,意味着走的是ajax接口;

  • c) 视频列表接口返回的数据是html代码,仍然需要用BeautifulSoup解析;

  • d) 请求视频列表接口时需要提交suid参数,这个参数值需要用uid在主播个人页获取;


4.2 python编码一下网的主播信息和视频列表


  • 构造class YiXia(Website),

  • 方法parse_user_page()拿着uid去获取主播个人信息;

  • 方法get_video_list()按分页获取视频列表数据


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 .







请到「今天看啥」查看全文