Python爬虫实战之多线程爬取猫眼电影Top100

前言

本次爬取猫眼电影采用requests库做网络请求,正则表达式做HTML网页解析,多线程方式进行爬取,最后数据序列化成json格式数据并保存。

分析

url分析

第二页

第三页

从图片中可以看出 url的变化规律为:


http://maoyan.com/board/4?offset=页数*10

当然,页数是从0开始的。

html分析

html分析

从图片中可以看出每一部电影区块都是由一个dd标签组成,而我们所要提取的信息都在这个dd标签里面。所以得出一个贪婪正则表达式为:

<dd>.*?>(\d+)</i>.*?data-src="(.*?)".*?"name"><a.*?>(.*?)</a>.*?class="star">(.*?)</p>.*?releasetime">(.*?)</p>.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>

其中()里面的就是每一个我们要提交的字段信息

代码解析

  • 导入我们待会需要使用的python库,注释里面标注了每个库对应作用
import threading #多线程
import requests, re, json#网络请求、正则、数据序列化
from requests.exceptions import RequestException #请求异常
from multiprocessing import Process #多进程
from multiprocessing import Pool #进程池
  • 编写一个方法获取网络HTML代码,代码注释写明了每句话的意思
def get_one_page(url): #传入url地址
    try:
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"
        } #定义一个字典形式的请求头,加入User-Agent为key的参数,用来模拟浏览器请求
        response = requests.get(url, headers=headers) #请求url
        with open('result.html', 'w', encoding='utf-8') as f: #把url以文件形式写入到本地,利于方便看html代码来分析
            f.write(response.text)
            f.close()
        if response.status_code == 200: #如果网路请求成功,返回html代码
            return response.text
        return None #否则返回空
    except RequestException: #捕获请求异常
        print("异常")
        return None
  • 编写一个解析网页html代码的生成器方法,把解析出来的每一项数据以字典类型,可迭代对象返回:
def parse_html(html):
    pattern = re.compile(
        '<dd>.*?>(\d+)</i>.*?data-src="(.*?)".*?"name"><a.*?>(.*?)</a>.*?class="star">(.*?)</p>.*?releasetime">(.*?)</p>.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>',
        re.S) #生成一个正则表达式对象
    items = re.findall(pattern, html) #通过正则表达式对象和网页html代码匹配到我们需要的数据,赋值给items
    # print(items)
    for item in items: #每一项数据用yield返回一个字典出去,形成一个生成器
        yield {
            "index": item[0],
            "image": item[1],
            "title": item[2],
            "actor": item[3].strip()[3:],
            "time": item[4].strip()[5:],
            "score": item[5] + item[6]
        }
  • 编写一个写入数据到本地的方法,用来把解析后的数据以序列化方式保存:

def write_to_file(content):
    with open('maoyantop100.txt', 'a', encoding='utf-8') as f:
        f.write(json.dumps(content, ensure_ascii=False) + '\n')
        f.close()
  • 定义一个主方法来执行对网页的获取,解析:
list=[] #定义一个列表,用于把每一个字典(也就是每一部电影的信息)存放进去
def main(offset):
    url = "http://maoyan.com/board/4?offset="+str(offset)
    html = get_one_page(url)

    for item in parse_html(html):
        list.append(item)
  • 最后在执行文件入口执行以上方法:

    • 以for循环方式爬取十页的网页内容
# for循环方式来爬取
    # for i in range(10):
    #     main(i*10)
    • 以多进程的方式爬取十页的网页内容
 #进程方式执行 起了十个进程来爬取,为了保证抓取完成也就是进程执行完毕,加入p.join()方法来保证进程执行完,当前程序才退出,但是这样会使爬取效率降低
    # processes = [Process(target=main,args=(i*10,) ) for i in range(10)]  # 用列表生成式 生成10个线程
    # for p in processes:
    #     p.start()  # 启动刚刚创建的10个进程
    #     p.join()  # 进程结束 主进程也就是当前的程序 才结束
    # print('master process finished...')
    #
    • 以多进程+进程池的方式爬取十页的网页内容
# #进程池 多进程的方式来爬取
    # def end(arg):# 单个进程结束执行的方法
    #     print("processes finish")
    # pool = Pool(5)
    # for i in range(10):
    #     pool.apply(main,args=(i*10,)) #串行执行
    #     # pool.apply_async(func=main, args=(i*10,), callback=end)  # 并行执行,callback,是进程结束后的回调,是主进程调用的回调。
    # pool.close()  # 需先close,再join
    # pool.join()  # join: 等待子进程,主线程再结束
    # print('main finished...')
    • 以多线程的方式爬取十页的网页内容
    #多线程方式爬取 启动十个线程来爬取,爬取速度及其快,可以实现秒获取
    threads=[ threading.Thread(target=main,args=(i*10,)) for i in range(10)] #用列表生成式 生成10个线程
    for t in threads: #启动刚刚创建的10个线程
        t.start()
        t.join() #加入join后 ,子线程结束,主线程才结束,运行速度会变慢
    print(json.dumps(list))
    write_to_file(list)

最终效果

第三页

完整代码地址:https://github.com/domain9065/maoyantop100

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