Python爬虫实战Pro | (1) 爬取猫眼电影Top100榜单

在本篇博客中,我们将使用requests+正则表达式来爬取猫眼电影官网的TOP100电影榜单,获取每部电影的序号,片名,主演,上映日期,评分和封面等内容。

之前在Python爬虫实战(1)中我们曾爬取过,本篇博客将对上次内容进行升级,使用yield和多线程。

打开猫眼Top100,分析URL的变化:发现Top100榜总共包含10页,每页10部电影,并且每一页的URL都是有规律的,如第2页为https://maoyan.com/board/4?offset=10,第三页为https://maoyan.com/board/4?offset=20。由此可得第n页为https://maoyan.com/board/4?offset=(n-1)*10。

  • 单个页面的爬取
def get_one_page(url):
    try:
        headers={
            'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
        }
        response = requests.get(url,headers=headers)
        if response.status_code == 200:
            return response.text
        return None
    except RequestException:
        return None
  • 解析函数

打开浏览器,右键检查,在element选项卡中,我们发现每部电影都在一对dd标签中:

接下来,我们可以用正则表达式进行提取,用.*?略过没用的部分,保存一些关键部分进行定位,用(.*?)获取想要的信息:

def parse_one_page(html):
    pattern = re.compile('<dd>.*?board-index.*?>(\d+)</i>' #序号
                         +'.*?data-src="(.*?)"'#封面图片
                         +'.*?name.*?<a.*?>(.*?)</a>' #片名
                         +'.*?star.*?>(.*?)</p>' #主演
                         +'.*?releasetime.*?>(.*?)</p>' #上映时间
                         +'.*?score.*?integer.*?>(.*?)</i>' #评分整数部分
                         +'.*?fraction.*?>(.*?)</i>' #评分小数部分
                         ,re.S)
    items = pattern.findall(html)

    #之前是把每个item保存在字典里,在追加到一个列表中。
    #现在对于每个item 用yield声明一个迭代器
    for item in items:
        yield {
            'index':item[0],
            'logo':item[1],
            'title':item[2],
            'star':item[3].strip()[3:] if len(item[3])>3 else '', #去除 前后空白字符 和 主演:
            'releasetime':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:
        f.write(json.dumps(content,ensure_ascii=False)+'\n') #ensure_ascii=False 可以显示中文字符
  • 设置页面偏移
def main(offset):
    url = 'https://maoyan.com/board/4?offset='+str(offset)  #新页面url
    html = get_one_page(url) #获取页面html
    #print(html)
    for item in parse_one_page(html): #由于解析函数返回的是一个迭代器 所以需要使用for循环对其进行实体化 
        print(item)
        write_to_file(item) #把每个item写入文件
  • 设置多线程
if __name__ == '__main__':
    '''
    for i in range(10):
        main(i*10)
        time.sleep(1)   
    '''

    pool = Pool()
    pool.map(main,[x*10 for x in range(10)])

正常情况下使用注释掉的部分,对10个页面顺序进行爬取。如果想加速,实现秒抓,可以添加多线程机制,将待爬取的页面分配给多个线程,并行爬取。建立起main函数及其传入参数之间的映射即可。

  • 完整代码
import requests
from requests import RequestException
import re
import json
from multiprocessing.pool import Pool

def get_one_page(url):
    try:
        headers={
            'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 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.*?>(\d+)</i>' #序号
                         +'.*?data-src="(.*?)"'#封面图片
                         +'.*?name.*?<a.*?>(.*?)</a>' #片名
                         +'.*?star.*?>(.*?)</p>' #主演
                         +'.*?releasetime.*?>(.*?)</p>' #上映时间
                         +'.*?score.*?integer.*?>(.*?)</i>' #评分整数部分
                         +'.*?fraction.*?>(.*?)</i>' #评分小数部分
                         ,re.S)
    items = pattern.findall(html)

    #之前是把每个item保存在字典里,在追加到一个列表中。
    #现在对于每个item 用yield声明一个迭代器
    for item in items:
        yield {
            'index':item[0],
            'logo':item[1],
            'title':item[2],
            'star':item[3].strip()[3:] if len(item[3])>3 else '', #去除 前后空白字符 和 主演:
            'releasetime':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:
        f.write(json.dumps(content,ensure_ascii=False)+'\n') #ensure_ascii=False 可以显示中文字符

def main(offset):
    url = 'https://maoyan.com/board/4?offset='+str(offset)  #新页面url
    html = get_one_page(url) #获取页面html
    #print(html)

    for item in parse_one_page(html): #由于解析函数返回的是一个迭代器 所以需要使用for循环对其进行实体化
        print(item)
        write_to_file(item) #把每个item写入文件


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

    pool = Pool()
    pool.map(main,[x*10 for x in range(10)])

  • 爬取结果

通过观察结果会发现,使用多线程并行爬取后,并不是按顺序爬取的,index并不是连续的。说明每个线程都在独立并行的爬取分配给它的页面。

如果不使用多线程,结果将是顺序爬取的。

 

 

 

 

 

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值