python3网络爬虫开发实战-抓取猫眼电影排行(正则表达式版)

抓取猫眼电影排行

目前没有学习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)

现在猫眼多了反爬虫,如果速度过快,则会无响应。所以后面加了一个延时等待。
输出结果:
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值