抓取猫眼电影排行
目前没有学习HTML解析库,这里先选用正则表达式作为解析工具
目标
提取出猫眼电影top100的电影名称、时间、评分、图片等信息。提取的站点URL为http://maoyan.com/board/4,提取的结果以文件形式保存下来。
抓取分析
首页显示的是top10,点击下面的 [第二页],URL变成了 http://maoyan.com/board/4?offset=10,这时显示的是排行11-20名的电影。当我们分开请求10次,offset参数分别设置为0、10、20…90,获取不同页面之后,再用正则表达式提取出相关信息,就可以得到top100 的所有电影信息。
抓取首页
估计是由于学习此书的人比较多,猫眼对反爬的策略有所增强。header中如果只有User-Agent ,有时会被限制,跳转的猫眼验证页面。因此,还需要增加Cookie。
import requests
def get_one_page(url):
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0',
'Cookie':'__mta=244153658.1609735789043.1609814345631.1609903887876.9; uuid_n_v=v1; uuid=44FD87D04E4811EBB586F7EF3F3BEBB881E6A67E34BF46848671B5C44D132AF8; _lxsdk_cuid=176cbbad8c4c8-0687fad5143e0b-3e604000-144000-176cbbad8c4c8; _lxsdk=44FD87D04E4811EBB586F7EF3F3BEBB881E6A67E34BF46848671B5C44D132AF8; _csrf=e80757462cd1516a77cd1805be934c54f7d4e90601451188280cb41fd6c4871a; Hm_lvt_703e94591e87be68cc8da0da7cbd0be2=1609735788,1609764730,1609812449,1609903888; Hm_lpvt_703e94591e87be68cc8da0da7cbd0be2=1609903888; _lxsdk_s=176d5bfd5c7-e23-d69-9d%7C%7C2'
}
result = requests.get(url, headers=headers)
if result.status_code==200:
return result.text
return None
def main():
url = 'http://maoyan.com/board/4'
html = get_one_page(url)
print(html)
main()
正则提取
在Network 监听组件中查看源代码
可以看到一部电影信息对应的是一个dd节点,我们用正则表达式来提取里面的电影信息(排名、电影图片链接、电影名称、主演、电影发布时间、评分)。
首先是排名:
<dd>.*?board-index.*?>(.*?)</i>
这时候代码:
import requests
import re
def get_one_page(url):
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0',
'Cookie':'__mta=244153658.1609735789043.1609814345631.1609903887876.9; uuid_n_v=v1; uuid=44FD87D04E4811EBB586F7EF3F3BEBB881E6A67E34BF46848671B5C44D132AF8; _lxsdk_cuid=176cbbad8c4c8-0687fad5143e0b-3e604000-144000-176cbbad8c4c8; _lxsdk=44FD87D04E4811EBB586F7EF3F3BEBB881E6A67E34BF46848671B5C44D132AF8; _csrf=e80757462cd1516a77cd1805be934c54f7d4e90601451188280cb41fd6c4871a; Hm_lvt_703e94591e87be68cc8da0da7cbd0be2=1609735788,1609764730,1609812449,1609903888; Hm_lpvt_703e94591e87be68cc8da0da7cbd0be2=1609903888; _lxsdk_s=176d5bfd5c7-e23-d69-9d%7C%7C2'
}
result = requests.get(url, headers=headers)
if result.status_code==200:
return result.text
return None
def parse_one_page(html):
pattern = re.compile('<dd>.*?board-index.*?>(.*?)</i>', re.S)
items = re.findall(pattern,html)
return items
def main():
url = 'http://maoyan.com/board/4'
html = get_one_page(url)
print(parse_one_page(html))
main()
运行结果:
['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
然后看电影图片链接,可以看到后面有个a节点,内部有两个img节点。第二个img节点的data-src 属性是图片链接。正则表达式改写如下:
<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)"
这时运行结果:
然后看电影名。后面p节点内,class 为name。可以用name作为标志位,提取a节点的整文内容。正则表达式改写如下:
<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name"><.*?>(.*?)</a>
运行结果
提取主演、发布时间、评分也是一样的道理。最后的正则表达式为:
<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name"><.*?>(.*?)</a>.*?star">(.*?)</p>.*?releasetime">(.*?)</p>.*?integer">(.*?)</i>.*?fraction">(.*?)</i>
这时程序的运行结果:
通过前面正则表达式那一节的学习我们可以知道,re.findall() 得到的是一个由元组组成的列表。当然,从上面的这个结果也可以看出来。此外我们发现数据比较乱(存在空格和换行),我们将匹配结果处理一下,遍历提取结果并生成字典。整体代码如下:
import requests
import re
def get_one_page(url):
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0',
'Cookie':'__mta=244153658.1609735789043.1609814345631.1609903887876.9; uuid_n_v=v1; uuid=44FD87D04E4811EBB586F7EF3F3BEBB881E6A67E34BF46848671B5C44D132AF8; _lxsdk_cuid=176cbbad8c4c8-0687fad5143e0b-3e604000-144000-176cbbad8c4c8; _lxsdk=44FD87D04E4811EBB586F7EF3F3BEBB881E6A67E34BF46848671B5C44D132AF8; _csrf=e80757462cd1516a77cd1805be934c54f7d4e90601451188280cb41fd6c4871a; Hm_lvt_703e94591e87be68cc8da0da7cbd0be2=1609735788,1609764730,1609812449,1609903888; Hm_lpvt_703e94591e87be68cc8da0da7cbd0be2=1609903888; _lxsdk_s=176d5bfd5c7-e23-d69-9d%7C%7C2'
}
result = requests.get(url, headers=headers)
if result.status_code==200:
return result.text
return None
def parse_one_page(html):
pattern = re.compile('<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name"><.*?>(.*?)</a>.*?star">(.*?)</p>.*?releasetime">(.*?)</p>.*?integer">(.*?)</i>.*?fraction">(.*?)</i>', re.S)
items = re.findall(pattern,html)
for item in items:
yield {
'index': item[0],
'image': item[1],
'title': item[2].strip(),
'actor': item[3].strip()[3:],
'time': item[4].strip()[5:],
'score': item[5].strip() +item[6].strip()
}
def main():
url = 'http://maoyan.com/board/4'
html = get_one_page(url)
for x in parse_one_page(html):
print(x)
main()
程序运行结果:
关于yield,在前一篇博文中有写:传送门
到此,我们就成功提取了单页的电影信息。
写入文件
我们将我们将提取的结果写入文本文件。通过JSON库的dumps() 方法实现字典的序列号,并指定ensure_ascii 参数为 False,这样可以保证输出结果是中文形式而不是Unicode编码。代码如下:
def write_to_file(content):
with open('result.txt', 'a', encoding='utf-8') as f_obj:
f_obj.write(json.dumps(content, ensure_ascii=False +'\n'))
此处的content 参数是一部电影的提取结果,是一个字典。
此时整体代码为:
import requests
import re
import json
def get_one_page(url):
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0',
'Cookie':'__mta=244153658.1609735789043.1609814345631.1609903887876.9; uuid_n_v=v1; uuid=44FD87D04E4811EBB586F7EF3F3BEBB881E6A67E34BF46848671B5C44D132AF8; _lxsdk_cuid=176cbbad8c4c8-0687fad5143e0b-3e604000-144000-176cbbad8c4c8; _lxsdk=44FD87D04E4811EBB586F7EF3F3BEBB881E6A67E34BF46848671B5C44D132AF8; _csrf=e80757462cd1516a77cd1805be934c54f7d4e90601451188280cb41fd6c4871a; Hm_lvt_703e94591e87be68cc8da0da7cbd0be2=1609735788,1609764730,1609812449,1609903888; Hm_lpvt_703e94591e87be68cc8da0da7cbd0be2=1609903888; _lxsdk_s=176d5bfd5c7-e23-d69-9d%7C%7C2'
}
result = requests.get(url, headers=headers)
if result.status_code==200:
return result.text
return None
def parse_one_page(html):
pattern = re.compile('<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name"><.*?>(.*?)</a>.*?star">(.*?)</p>.*?releasetime">(.*?)</p>.*?integer">(.*?)</i>.*?fraction">(.*?)</i>', re.S)
items = re.findall(pattern,html)
for item in items:
yield {
'index': item[0],
'image': item[1],
'title': item[2].strip(),
'actor': item[3].strip()[3:],
'time': item[4].strip()[5:],
'score': item[5].strip() +item[6].strip()
}
def write_to_file(content):
with open('result.txt', 'a', encoding='utf-8') as f_obj:
f_obj.write(json.dumps(content, ensure_ascii=False)+'\n')
def main():
url = 'http://maoyan.com/board/4'
html = get_one_page(url)
for x in parse_one_page(html):
write_to_file(x)
main()
程序运行之后,成功输出了result.txt 文件
分页爬取
因为我们需要爬取的是猫眼电影的top100,所以还需要遍历,给链接传入offset 参数,此时添加如下调用:
if __name__=='__main__':
for i in range(10):
main(offset=i*10)
对main() 方法进行修改,接受一个offset 值作为偏移量,然后构造URL进行爬取。代码如下:
def main(offset):
url = 'http://maoyan.com/board/4?offset='+str(offset)
html = get_one_page(url)
for x in parse_one_page(html):
write_to_file(x)
完整代码:
import requests
import re
import json
import time
from requests.exceptions import RequestException
def get_one_page(url):
try:
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0',
'Cookie':'__mta=244153658.1609735789043.1609814345631.1609903887876.9; uuid_n_v=v1; uuid=44FD87D04E4811EBB586F7EF3F3BEBB881E6A67E34BF46848671B5C44D132AF8; _lxsdk_cuid=176cbbad8c4c8-0687fad5143e0b-3e604000-144000-176cbbad8c4c8; _lxsdk=44FD87D04E4811EBB586F7EF3F3BEBB881E6A67E34BF46848671B5C44D132AF8; _csrf=e80757462cd1516a77cd1805be934c54f7d4e90601451188280cb41fd6c4871a; Hm_lvt_703e94591e87be68cc8da0da7cbd0be2=1609735788,1609764730,1609812449,1609903888; Hm_lpvt_703e94591e87be68cc8da0da7cbd0be2=1609903888; _lxsdk_s=176d5bfd5c7-e23-d69-9d%7C%7C2'
}
result = requests.get(url, headers=headers)
if result.status_code==200:
return result.text
return None
except RequestException:
return None
def parse_one_page(html):
pattern = re.compile('<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name"><.*?>(.*?)</a>.*?star">(.*?)</p>.*?releasetime">(.*?)</p>.*?integer">(.*?)</i>.*?fraction">(.*?)</i>', re.S)
items = re.findall(pattern,html)
for item in items:
yield {
'index': item[0],
'image': item[1],
'title': item[2].strip(),
'actor': item[3].strip()[3:],
'time': item[4].strip()[5:],
'score': item[5].strip() +item[6].strip()
}
def write_to_file(content):
with open('result.txt', 'a', encoding='utf-8') as f_obj:
f_obj.write(json.dumps(content, ensure_ascii=False)+'\n')
def main(offset):
url = 'http://maoyan.com/board/4?offset=' + str(offset)
html = get_one_page(url)
for x in parse_one_page(html):
write_to_file(x)
if __name__=='__main__':
for i in range(10):
main(offset=i*10)
time.sleep(1)
现在猫眼多了反爬虫,如果速度过快,则会无响应。所以后面加了一个延时等待。
输出结果: