抓取猫眼TOP100电影信息

本文介绍利用Requests库、multiprocessing库和正则表达式爬取猫眼电影TOP100电影的相关信息,提取出电影名称、上映时间、评分、封面图片等信息,将爬取的内容写入到文件中。站点URL为 http://maoyan.com/board/4

注释:源码视频书籍练习题等资料进群696541369 即可免费获取

准备

本文使用了Requests库,使用pip安装: pip install requests

分析

打开http://maoyan.com/board/4,可以看到榜单信息。如下图所示

排名第一的电影是霸王别姬,可以提取的信息有电影名称、主演、上映时间、评分、封面图等。

点击页面下方的分页列表翻页到第二页,会发现URL会变成,比首页多了个offset=10 参数,而目前显示的是排名11-20的电影,初步判断这是偏移量参数。再点击下一页,URL变成了

https://maoyan.com/board/4?offset=20

offset变成了20,显示的是排名21-30的电影。

由此可见,offset代表偏移量,偏移量为n,则显示的是排名n+1~n+10的电影,每页显示10个电影。所以,想要获取TOP100电影信息,只要分开获取10次,只需把10次请求的URL中offset参数分别设为 0,10,20,30...90即可(首页的offset值为0)。获取到不同的网页后使用正则表达式提取出我们要的信息,就可以得到TOP100电影信息了,可以使用多线程加速爬取。

爬取实现

爬取首页

实现get_page()方法,传入url参数可以将抓取的页面结果返回。以下代码获取首页内容:

import requests

from requests.exceptions import RequestException

def get_page(url):

headers = {

'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'

}

response = requests.get(url,headers = headers)

try:

if response.status_code == 200:

return response.text

return None

except RequestException:

print("request error")

return None

def main():

html = get_page('https://maoyan.com/board/4')

print(html)

main()

运行之后就成功获取到了首页的源代码,接下来使用正则表达式进行解析,提取出我们想要的信息。

正则提取

回到浏览器页面,在开发者工具Network监听组件中查看源代码。如图:

值得注意的是这里不是从Elements选项卡里查看的源代码,因为Elements里看到的源代码很有可能经过Javascript处理过从而和原始请求不同,所以要从Network选项卡里查看原始请求得到的源码。

查看此处代码:

不难发现,要爬取的每部电影信息都在<dd>标签里,接下来使用正则表达式提取信息。

首先,提取它的排名信息,它的排名信息在class为board-index的i标签里,使用非贪婪匹配来提取i内的信息,正则表达式可以写为:<dd>.*?board-index.*?>(d+)</i>

接下来提取电影的封面图片。在排名后面的a便签里有两个img便签,经过检查,第二个img是电影的封面图片,正则:.*?data-src="(.*?)"

然后提取电影的名称,它在class为name的<p>便签内,可以使用name作为标志位进一步提取到其内a的文本内容,正则写为:.*?name.*?a.*?>(.*?)</a>

提取主演:.*?star">(.*?)</p>

提取上映时间:.*?releasetime">(.*?)</p>

提取评分:.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>

最后正则表达式写为:

.*?board-index.*?>(d+).*?data-src="(.*?)".*?name.*?a.*?>(.*?).*?star">(.*?).*?releasetime">(.*?).*?integer">(.*?).*?fraction">(.*?).*?

上面的正则表达式可以匹配一个电影,匹配了7条信息,接下来可以通过findall()方法提取所有内容。可以定义一个用来解析页面的方法parse_page(),代码如下:

def parse_page(html):

pattern = re.compile('

.*?board-index.*?>(d+).*?data-src="(.*?)".*?name.*?a.*?>(.*?).*?star">(.*?)'

+ '.*?releasetime">(.*?).*?integer">(.*?).*?fraction">(.*?).*?',r.S) #re.S使.能匹配任意字符

items = pattern.findall(str(html))

这样就成功得拿到了一页10个电影的信息,这是一个列表,获取到的结果如下:

[('1', 'https://p1.meituan.net/movie/20803f59291c47e1e116c11963ce019e68711.jpg@160w_220h_1e_1c', '霸王别姬', ' 主演:张国荣,张丰毅,巩俐 ', '上映时间:1993-01-01', '9.', '6'), ('2', 'https://p0.meituan.net/movie/283292171619cdfd5b240c8fd093f1eb255670.jpg@160w_220h_1e_1c', '肖申克的救赎', ' 主演:蒂姆·罗宾斯,摩

根·弗里曼,鲍勃·冈顿 ', '上映时间:1994-10-14(美国)', '9.', '5'), ('3', 'https://p0.meituan.net/movie/54617769d96807e4d81804284ffe2a27239007.jpg@160w_220h_1e_1c', '罗

马假日', ' 主演:格利高里·派克,奥黛丽·赫本,埃迪·艾伯特 ', '上映时间:1953-09-02(美国)', '9.', '1'), ('4', 'https://p0.meituan.net/movie/e55ec5d18ccc

83ba7db68caae54f165f95924.jpg@160w_220h_1e_1c', '这个杀手不太冷', ' 主演:让·雷诺,加里·奥德曼,娜塔莉·波特曼 ', '上映时间:1994-09-14(法国)', '9.', '

5'), ('5', 'https://p1.meituan.net/movie/f5a924f362f050881f2b8f82e852747c118515.jpg@160w_220h_1e_1c', '教父', ' 主演:马龙·白兰度,阿尔·帕西诺,詹姆斯·肯恩

', '上映时间:1972-03-24(美国)', '9.', '3'), ('6', 'https://p1.meituan.net/movie/0699ac97c82cf01638aa5023562d6134351277.jpg@160w_220h_1e_1c', '泰坦尼克号', '

主演:莱昂纳多·迪卡普里奥,凯特·温丝莱特,比利·赞恩 ', '上映时间:1998-04-03', '9.', '5'), ('7', 'https://p0.meituan.net/movie/da64660f82b98cdc1b8a3804e69609e04110

8.jpg@160w_220h_1e_1c', '唐伯虎点秋香', ' 主演:周星驰,巩俐,郑佩佩 ', '上映时间:1993-07-01(中国香港)', '9.', '2'), ('8', 'https://p0.meituan.net/movie/b076ce63e9860ecf1ee9839badee5228329384.jpg@160w_220h_1e_1c', '千与千寻', ' 主演:柊瑠美,入野自由,夏木真理 ', '上映时间:2001-07-20(日本)', '9.', '3'), ('9', 'https://p0.meituan.net/movie/46c29a8b8d8424bdda7715e6fd779c66235684.jpg@160w_220h_1e_1c', '魂断蓝桥', ' 主演:费雯·丽,罗伯特·泰勒,露塞尔·沃特森

', '上映时间:1940-05-17(美国)', '9.', '2'), ('10', 'https://p0.meituan.net/movie/230e71d398e0c54730d58dc4bb6e4cca51662.jpg@160w_220h_1e_1c', '乱世佳人', '

主演:费雯·丽,克拉克·盖博,奥利维娅·德哈维兰 ', '上映时间:1939-12-15(美国)', '9.', '1')]

这样的数据看上去很杂乱,使用字典将数据格式化:

for item in items:

yield {

'top':item[0],

'image_src':item[1],

'name':item[2],

'actor':item[3].strip()[3:] if len(item[3]) > 3 else '',

'releasetime':item[4].strip()[5:],

'score':item[5] + item[6]

}

这样就可以获得电影信息的结构化数据了,每个电影的信息都包含在一个字典里。获得的结果如下:

{'top': '1', 'image_src': 'https://p1.meituan.net/movie/20803f59291c47e1e116c11963ce019e68711.jpg@160w_220h_1e_1c', 'name': '霸王别姬', 'actor': '张国荣,张丰毅,巩俐', 'releasetime': '1993-01-01', 'score': '9.6'}

{'top': '2', 'image_src': 'https://p0.meituan.net/movie/283292171619cdfd5b240c8fd093f1eb255670.jpg@160w_220h_1e_1c', 'name': '肖申克的救赎', 'actor': '蒂姆·罗宾斯,摩根·弗里曼,鲍勃·冈顿', 'releasetime': '1994-10-14(美国)', 'score': '9.5'}

{'top': '3', 'image_src': 'https://p0.meituan.net/movie/54617769d96807e4d81804284ffe2a27239007.jpg@160w_220h_1e_1c','name': '罗马假日', 'actor': '格利高里·派克,奥黛丽·赫本,埃迪·艾伯特', 'releasetime': '1953-09-02(美国)', 'score': '9.1'}

{'top': '4', 'image_src': 'https://p0.meituan.net/movie/e55ec5d18ccc83ba7db68caae54f165f95924.jpg@160w_220h_1e_1c', 'name': '这个杀手不太冷', 'actor': '让·雷诺,加里·奥德曼,娜塔莉·波特曼', 'releasetime': '1994-09-14(法国)', 'score': '9.5'}

{'top': '5', 'image_src': 'https://p1.meituan.net/movie/f5a924f362f050881f2b8f82e852747c118515.jpg@160w_220h_1e_1c', 'name': '教父', 'actor': '马龙·白兰度,阿尔·帕西诺,詹姆斯·肯恩', 'releasetime': '1972-03-24(美国)', 'score': '9.3'}

{'top': '6', 'image_src': 'https://p1.meituan.net/movie/0699ac97c82cf01638aa5023562d6134351277.jpg@160w_220h_1e_1c', 'name': '泰坦尼克号', 'actor': '莱昂纳多·迪卡普里奥,凯特·温丝莱特,比利·赞恩', 'releasetime': '1998-04-03', 'score': '9.5'}

{'top': '7', 'image_src': 'https://p0.meituan.net/movie/da64660f82b98cdc1b8a3804e69609e041108.jpg@160w_220h_1e_1c', 'name': '唐伯虎点秋香', 'actor': '周星驰,巩俐,郑佩佩', 'releasetime': '1993-07-01(中国香港)', 'score': '9.2'}

{'top': '8', 'image_src': 'https://p0.meituan.net/movie/b076ce63e9860ecf1ee9839badee5228329384.jpg@160w_220h_1e_1c', 'name': '千与千寻', 'actor': '柊瑠美,入野自由,夏木真理', 'releasetime': '2001-07-20(日本)', 'score': '9.3'}

{'top': '9', 'image_src': 'https://p0.meituan.net/movie/46c29a8b8d8424bdda7715e6fd779c66235684.jpg@160w_220h_1e_1c', 'name': '魂断蓝桥', 'actor': '费雯·丽,罗伯特·泰勒,露塞尔·沃特森', 'releasetime': '1940-05-17(美国)', 'score': '9.2'}

{'top': '10', 'image_src': 'https://p0.meituan.net/movie/230e71d398e0c54730d58dc4bb6e4cca51662.jpg@160w_220h_1e_1c', 'name': '乱世佳人', 'actor': '费雯·丽,克拉克·盖博,奥利维娅·德哈维兰', 'releasetime': '1939-12-15(美国)', 'score': '9.1'}

写入文件

得到数据后最后将数据保存到文件,通过JOSN库的dumps()方法可以实现字典的序列化。因为这里要处理中文,将ensure_ascii参数设为False就可以保证输出结果是中文形式而不是Unicode编码。代码如下:

def write_to_file(content):

with open('result.txt','a',encoding='utf-8') as f:

f.write(json.dumps(content,ensure_ascii = False) + '')

f.close()

其中open()指定写入方式为a尾部写入,这是因为此时是for循环写入数据,如果用w写入只会保留最后一组的数据。或者在这之前打开文件,等写入完数据后再关闭也可以。

通过调用write_to_file()方法即可实现将字典写入到文本文件的过程。

main方法

实现main()方法接收一个offset值作为偏移量,然后构造URL进行爬取。代码如下:

def main(offset):

url = "http://maoyan.com/board/4?offset=" + str(offset)

html = get_page(url)

for item in parse_page(html):

print(item)

write_of_file(item)

多线程分页爬取

上面实现了给main()传入一个offset值爬取单页10个电影的数据,接下来使用多线程来抓取整个TOP100的电影数据。

from multiprocessing import Pool # 引入多线程模块

if __name__ == '__main__':

#创建线程池

pool = Pool()

# pool.map第一个参数是函数,第二个参数是传递给函数的参数

pool.map(main,[i*10 for i in range(10)])

Pool.map()函数第一个参数是函数,第二个参数是传递给函数的参数,在上面代码中是一个迭代器,将迭代器中的数字作为参数依次传入函数中。

注意:使用多线程爬取会导致最后写入到文件内的电影数据(top值)是乱序的,如需保证爬取到的电影信息写入到文件是按照top值排序的,放弃多线程将代码改为:

import time #引入时间模块

if __name__ == '__main__':

for i in range(10):

main(offset=i * 10)

time.sleep(1)

为突破猫眼反爬虫机制(速度过快会无响应),上面代码增加了一个延时等待。

大功告成!完整代码如下:

import requests

import re

import time

import json

from requests.exceptions import RequestException

from multiprocessing import Pool

def get_page(url):

headers = {

'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'

}

response = requests.get(url,headers = headers)

try:

if response.status_code == 200:

return response.text

return None

except RequestException:

print("request error")

return None

def parse_page(html):

pattern = re.compile('

.*?board-index.*?>(d+).*?data-src="(.*?)".*?name.*?a.*?>(.*?).*?star">(.*?)'

+ '.*?releasetime">(.*?).*?integer">(.*?).*?fraction">(.*?).*?',re.S) #re.S使.能匹配任意字符

items = pattern.findall(str(html))

for item in items:

yield {

'top':item[0],

'image_src':item[1],

'name':item[2],

'actor':item[3].strip()[3:] if len(item[3]) > 3 else '',

'releasetime':item[4].strip()[5:],

'score':item[5] + item[6]

}

def write_to_file(content):

with open('result.txt','a',encoding='utf-8') as f:

f.write(json.dumps(content,ensure_ascii = False) + '')

f.close()

def main(offset):

url = "http://maoyan.com/board/4?offset=" + str(offset)

html = get_page(url)

for item in parse_page(html):

print(item)

write_to_file(item)

# 如需保证电影顺序,则放弃使用多线程

# if __name__ == '__main__':

# for i in range(10):

# main(offset=i * 10)

# time.sleep(1)

if __name__ == '__main__':

pool = Pool()

pool.map(main,[i*10 for i in range(10)])

猜你喜欢

转载自blog.csdn.net/weixin_42209553/article/details/86646182