Python爬取猫眼TOP100的电影


title: Python爬取猫眼TOP100电影
date: 2019-02-17 20:34:24
tags: [Python,爬虫]
categories: Python

目标

提取出猫眼电影 TOP100 的电影名称、时间、评分、图片等信息,提取的站点为 http://maoyan.com/board/4,并将提取的结果以文件的形式保存下来。

抓取分析

打开抓取的站点,发现第一页显示排行1-10的电影,第二页显示排行11-20的电影,但是url发生明显改变变为https://maoyan.com/board/4?offset=10比之前的多了一个参数offset=10,再点击第三页发现offset=20,可以推断出规律,offset代表偏移量值,如果偏移量为n,那么电影序号就为(n+1) ~(n+10),所以当我们想获取top100的电影,就需要分开10次请求,这样获取不同的页面后,再通过正则表达式提取出相关的信息,就可以了获取TOP100的所有电影信息了。

抓取首页

抓取首页的源代码

import requests

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

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

正则提取

获取首页的源代码之后,通过正则提取我们需要的信息。回到浏览器,再开发者模式下的Network监听组件中,查看源代码,我们可以发现一部电影的信息对应的源代码就是一个dd节点,我们用正则表达式来提取这里的一些电影信息。image-20190217183708327

  • 提取它的排名信息,排名第一的电影为“霸王别姬”,它的排名信息在 class 为 board-index / board-index-1 的 i 节点内,这里采用非贪婪匹配提取 i 节点内的信息,正则表达式为:<dd>.*board-index.*?>(.*?)</i>
  • 提取电影的图片,在后边的 a 标签内发现有两个 img 节点,经检查后发现,第二个 img 节点是电影封面,故这里提取第二个节点的 data-src 属性,正则表达式为:<dd>.*board-index.*?>(.*?)</i>.*?data-src="(.*?)"
  • 提取电影的名称,可以发现在后边的 class 为 name 的 p 节点中,其实这里有好几个地方都可以进行提取,书上提取了 a 节点中的文本信息,正则表达式为:<dd>.*board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>
  • 再提取主演、发布时间、评分等内容时,都是同样的原理,最后的完整的正则表达式为:<dd>.*board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>

接下来,通过调用findall()方法提取出所有的内容:

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)
    print(items)

image-20190217185920478

可以看出当前提取出来的数据,还十分的乱,所以我们需要将匹配结果再处理一下,遍历提取结果并生成字典:

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:],
            'time': item[4].strip()[5:],
            'score': item[5].strip() + item[6].strip()
        }

写入文件

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

import json

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')

run()

通过 run() 方法来调用前面实现的方法,将单页的电影结果写入文件。

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

分页爬取

因为抓取的是 猫眼TOP100 的电影,所以还需要遍历一下,给这个链接传入offset的参数

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

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

整合代码

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

def get_one_page(url):
    try:
        headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36'
        }
        response = requests.get(url, headers=headers)
        if response.status_code == 200:
            return response.text
        return None
    except RequestException:
        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:],
            '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:
        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):
        print(item)
        write_to_file(item)

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

个人博客

原文链接:加载失败,请重新获取