【Python3 爬虫学习笔记】基本库的使用 13 —— 抓取猫眼电影排行

四、抓取猫眼电影排行

4.1 抓取分析

需要抓取的目标站点为http://maoyan.com/board/4 ,打开之后便可以查看到榜单信息,如下图所示:
在这里插入图片描述
排名第一的电影是霸王别姬,页面中显示的有效信息有影片名称、主演、上映时间、上映地区、评分、图片等信息。
将网页滚动到最下方,可以发现有分页的列表,直接点击第二页,变化如下:
在这里插入图片描述
可以发现页面的URL变成http://maoyan.com/board/4?offset=10,比之前的URL多了一个参数,那就是offset=10,而目前显示的结果是排行11~ 20名的电影,初步推断这是一个偏移量的参数。再点击下一页,发现页面的URL变成http://maoyan.com/board/4?offset=20,参数offset变成了20,而显示的结果是排行21~30的电影。
由此可以总结出规律,offset代表偏移量,如果偏移量为n,则显示的电影序号就是n+1到n+10,每页显示10个。所以想获取TOP100电影,只需要分开请求10次,而10次的offset参数分别设置为0,10,20…90即可,这样获取不同的页面之后,再用正则表达式提取出相关信息,就可以得到TOP100的所有电影信息了。

4.2 抓取首页

编写get_one_page()方法,给它传入url参数,然后将抓取的页面结果返回,再通过main()方法调用。初步代码实现如下:

import requests

def get_one_page(url):
     headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36'}
     response = requests.get(url, headers=headers)
     if response.status_code == 200:
         return response.text
     return None

def main():
    url = 'http://maoyan.com/board/4'
    html = get_one_page(url)
    print(html)

main()

这样运行之后,就可以成功获取首页的源代码。获取源代码后,就需要解析页面,提取出我们想要的信息。

4.3 正则提取

接下来,回到网页看一下页面的真实源码。按下F12进入开发者模式下的Network监听组件中查看源代码,如下图所示:
在这里插入图片描述
注意,这里不要在Elements选项卡中直接查看源码,因为那里的源码可能经过JavaScript操作而与请求不同,而是需要从Network选项卡部分查看原始请求得到的源码。
可以看到,一部电影信息对应的源代码是一个dd节点,我们用正则表达式来提取这里面的一些电影信息。首先,需要提取它的排名信息。而它的排名信息是在class为board-index的i节点内,这里利用非贪婪匹配来提取i节点内的信息,正则表达式为:

<dd>.*?board-index.*?>(.*?)</i>

随后需要提取电影的图片。可以看到,后面有a节点,其内部有两个img节点。经过检查后发现,第二个节点的额data-src属性是图片的链接。这里提取第二个img节点的data-src属性,正则表达式改为:

<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)"

再往后,需要提取电影的名称,它在后面的p节点内,class为name。所以,可以用name做一个标志位,然后进一步提取到其内a节点的正文内容,此时表达式改为如下:

<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>

再提取主演、发布时间、评分等内容时,都是同样的原理。最后,正则表达式写为:

<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?interger.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>

这样一个正则表达式可以匹配一个电影的结果,里面匹配了7个信息。接下来,通过调用findall()方法提取出所有的内容。
接下来,我们再定义解析页面的方法parse_one_page(),主要是通过正则表达式来从结果中提取出我们想要的内容,实现代码如下:

def parse_one_page(html):
    pattern = re.compile('<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?interger.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>'), re.S)
    items = re.findall(pattern, html)
    print(items)

这样便可以成功的将一页的10个电影信息都提取出来。
但这样还不够,数据比较乱,使用遍历提取结果并生成词典,此时可改为如下:

def parse_one_page(html):
    pattern = re.compile('<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a'
                         + '.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>' 
                         + '.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>', 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:] if len(item[3]) > 3 else '',
        'time':item[4].strip()[5:] if len(item[4]) > 5 else '',
        'score':item[5].strip() + item[6].strip()
        }

这样就可以成功提取出电影的排名、图片、标题、演员、时间、评分等内容了,并把它赋值为一个个字典,形成结构化数据。

4.4写入文件

将提取的结果写入文件,这里直接写入到一个文本文件中。这里通过JSON库的dumps()方法实现字典的序列化,并制定ensure_ascii参数为False,这样可以保证输出结果是中文形式而不是Unicode编码。代码如下:

扫描二维码关注公众号,回复: 3610603 查看本文章
def write_to_file(content):
    with open('result.txt', 'a', encoding='utf-8') as f:
        print(type(json.dumps(content)))
        f.write(json.dumps(content, ensure_ascii=False)+'\n')

通过调用write_to_file()方法即可实现将字典写入到文本文件的过程,此处的content参数就是一部电影的提取结果,是一个字典。

4.5整合代码

最后,实现main()方法来调用前面实现的方法,将单页的电影结果写入到文件。

def main():
    url = 'http://maoyan.com/board/4'
    html = get_one_page(url)
    for item in parse_one_page(html):
        write_to_file(item)

到此为止,我们完成了单页电影的提取,也就是首页的10部电影可以成功提取并保存到文本文件中了。

4.6 分页爬取

因为我们需要抓取的是TOP100的电影,所以还需要遍历一下,给这个链接传入offset参数,实现其他90部电影的爬取,此时添加如下调用即可:

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 item in parse_one_page(html):
        write_to_file(item)

完整代码:

import requests
import re
import json
from requests.exceptions import RequestException
import time

def get_one_page(url):
     headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36'}
     response = requests.get(url, headers=headers)
     if response.status_code == 200:
         return response.text
     return None
 
def parse_one_page(html):
    pattern = re.compile('<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a'
                         + '.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>' 
                         + '.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>', 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:] if len(item[3]) > 3 else '',
        'time':item[4].strip()[5:] if len(item[4]) > 5 else '',
        'score':item[5].strip() + item[6].strip()
        }

def write_to_file(content):
    with open('result.txt', 'a', encoding='utf-8') as f:
        print(type(json.dumps(content)))
        f.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 item in parse_one_page(html):
        write_to_file(item)

if __name__ == '__main__':
    for i in range(10):
        main(offset=i * 10)
        time.sleep(1)

现在猫眼多了反爬虫,如果速度过快,则会无响应,所以这里又增加了一个延时等待。

运行结果

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/htsait4113/article/details/82795382