本文主要采用requests库和正则表达式爬取猫眼电影的top100排名的电影的信息,包括电影、评分、主要演员等信息,并保存到本地文件;同时,下载电影的图片到本地目录。
主要内容:
一、目标站点分析;
二、数据爬取的流程;
三、讨论与扩展
一、目标站点分析:
进入目标站点猫眼电影https://maoyan.com/board/4
点击下一页,观察第二页的url:
可以发现不同的分页由offset取不同的整数10倍数来区分,每个页面显示10条电影信息,所以offset以10为偏移量,第一页的offset是10,第二页就是20,依此类推。其中每部电影的信息采用标签dd来描述:
主要思路:爬取第一页数据,然后采用正则表达式提取前10部电影的信息,要爬取前100的电影,后续的只要重复第一页的操作即可。
二、数据爬取流程:
1、爬取首页:
定义一个get_one_page方法,参数url给出要爬取的页面,源码如下:
通过判断response返回消息的状态码是否为200,判断是否访问成功,将代码包括在try…except…语句中,捕捉可能发生的异常。
出现的问题:
将猫眼top100首页的url:https://maoyan.com/board/4? 传入到get_one_page()方法后,消息状态码是200,但打印出来的信息是乱码,说明网站采取反爬措施。如何破?通过给request设置必要的headers信息,就可以解决这一问题,具体参照浏览器访问猫眼top100时所显示的header信息。
尝试使用Accept和User-Agent这两个字段,构建headers,并修改代码request.get(url)为requests.get(url, headers=headers),重新调用方法后,打印的信息与浏览器中的网页信息一致,说明headers的设置起作用。
2、使用正则表达式解析网页:
引入re模块,创建正则表达式,因为我们要匹配:排名,电影封面,片名,主演姓名,上映时间,评分等6个信息,都写在一个正则表达式中,整个代码会比较长,需要耐心处理:
1 | pattern = re.compile('<dd>.\*?board-index.\*?>(\\d+)</i>.\*?data-src="(.\*?)".\*?class="name">.\*?>(.\*?)</a>.\*?'+'class="star">(.\*?)</p>.\*?class="releasetime">(.\*?)</p>.\*?class="integer">(.\*?)</i>'+'.\*?class="fraction">(.\*?)</i></p>.\*?</dd>', re.S) |
该代码的几个注意点:
a. 目标数据点,即要提取的信息,需要使用”()”包裹起来;比如第一个是关于排名的数字,其正则表达式”\d+”采用“()”包裹;
b. 无关紧要的中间字符,采用”.*?”过滤掉;
c. 因为正则表达式过程,中间必需切分换行,换行相当于对一个长字符串切分;
d.”re.S”需要加上,表示跨\n符进行匹配;
完整的代码如下:
1 | def parse_one_page(html): |
返回的结果是list,信息比较杂乱,使用yield方法,对其进行字典格式化:
1 | items = re.findall(pattern, html) |
上述代码索引actor的strip()方法主要用于除去换行符,而[3:]主要截取字符串,删除”主演:”这三个字符。
3、存储数据到文件:
定义一write_to_file()方法,参数为步骤2中的字典格式化内容,代码如下:
1 | def write_to_file(content): |
因为是字典序,使用json.dumps将dict类型数据转化为string类型数据,File文件的打开类型选择’a’,表示追加写的方式,运行代码后,当前的PyCharm目录下自动增加result.txt文件,但是里面的中文以乱码显示,所以设定编码方式为utf-8,修改后的代码:
1 | def write_to_file(content): |
4、开启循环,抓取前100电影信息:
根据每个分页以offset偏移量为10,构建循环处理的代码,每次循环即处理新的offset url值的过程,代码如下:
1 | for i in range(10): |
5、下载电影封面图到本地:
图片属于二进制文件,给定url路径使用request的get方法即可获得数据,定义save_image_file(url, path)方法,其中path指定图片的保存路径。
1 | def save_image_file(url, path): |
上述代码的调用示例:
1 | save_image_file(item['image'], 'cover/' + item['title'] + '.jpg') |
三、讨论与扩展:
1、为加快数据爬取速度,可实现多线程或者多进程抓取,比如可使用多进程包multiprocessing:
示例代码如下:
1 | from multiprocessing import pool |
但使用多进程,容易被网站侦测,导致封ip,所以需同时配合ip代理池使用。
2、短时间爬取次数过多后,会发生程序出错,原因可能是被网站侦测到爬虫,导致封ip行为,所以必须配合ip代理池;