豆瓣电影Python爬虫(ajax动态加载,详细过程附源码)

目标URL:豆瓣电影 (douban.com) 分类排行榜

Step1:前期准备

1、网站预览:随便点击某个分类排行榜,这里选择动作片,下滑发现动态加载数据

2、查看XHR请求:右键选择检查,选中Network,刷新网页,找到包含数据的XHR

3、查看数据:打开获取到的XHR请求,查看数据,可以看到为JSON数据,且包含我们想要的所有数据

4、JSON解析:若JSON没有格式化,可以到JSON在线解析及格式化验证 - JSON中文网进行解析

5、分析URL:https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&start=0&limit=20

针对于该URL进行观察,可以发现type=5即爬取的电影类型(此处为动作片)

interval_id=100%3A90即好于100%-90%的影片

start=0&limit=20两个参数即起始电影与每页的数据量(电影量)。在搜索框内直接修改url中start=1发现从排行第2的电影开始。start=0的情况下,直接修改limit=想要爬取排行前多少的电影(如limit=100即排行前100的电影),发现前100个电影全部显示

Step2:爬虫代码

1、URL构建:根据前面的分析,可以直接对URL:

https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&start=0&limit=20 

修改type=想要爬取的电影类型(可在搜索框查看对应数字)和limit=想要爬取排行前多少的电影

注意此处interval_id=100%3A90应使用未经过URL编码的值(即使用"100:90"而不是"100%3A90")requests自行处理所有必要的编码工作

url = "https://movie.douban.com/j/chart/top_list"
base_params = {
    "type": "5",   #电影类型
    "interval_id": "100:90",  #评分区间,应使用未编码的值
    "start": 0,  #默认从第一个对应开始
    "limit": 100   #修改为想要获取的电影数量
}

2、请求头headers,只需要保留User-Agent即可

headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0"
}

3、请求URL并解析
注意:Python类型list不能直接转换成MySQL可以接受的格式。像typesactors这样的字段是列表类型,在将数据传递到数据库之前,需要将任何列表字段转换成JSON字符串或逗号分隔的字符串。在应用 json.dumps() 时添加参数 ensure_ascii=False,这样做可以防止 JSON 库将非 ASCII 字符转义成 Unicode 编码,从而保持中文字符的可读性。

try:
    response = requests.get(url, params=base_params, headers=headers, timeout=10)
    response.raise_for_status()
    data = response.json()
    # 选择特定字段存储
    selected_columns = [
        "id",  # 电影序号
        "title",  # 电影名称
        "score",  # 电影评分
        "rank",  # 电影排名
        "cover_url",  # 电影封面
        "types",  # 电影类型
        "regions",  # 电影地区
        "url",  # 电影链接
        "release_date",  # 电影上映日期
        "actor_count",  # 电影演员数量
        "vote_count",  # 电影评分人数
        "actors",  # 电影演员
    ]
    df = pd.DataFrame(data)[selected_columns]
    # 转换列表为中文JSON字符串
    df['types'] = df['types'].apply(lambda x: json.dumps(x, ensure_ascii=False))
    df['actors'] = df['actors'].apply(lambda x: json.dumps(x, ensure_ascii=False))
    df['regions'] = df['regions'].apply(lambda x: json.dumps(x, ensure_ascii=False))
    return df
except Exception as e:
    print(f"An error occurred: {e}")
    return pd.DataFrame()

4、存储到excel和mysql

#存储到excel
def save_to_excel(df, filename):
    df.to_excel(filename, index=False)
    print(f"Data saved to {filename}")
提前安装 mysql-connector-python 包:
pip install mysql-connector-python -i https://pypi.tuna.tsinghua.edu.cn/simple
#存储到mysql
def save_to_mysql(df, db_url, table_name):
    engine = create_engine(db_url)
    df.to_sql(table_name, con=engine, index=False, if_exists='replace')
    print(f"Data saved to MySQL table {table_name}")
Mysql建表语句:
CREATE TABLE movies
(
    id           VARCHAR(255) NOT NULL PRIMARY KEY,
    title        VARCHAR(255),
    score        DECIMAL(3, 1),
    `rank`       INT, -- 使用反引号来避免使用保留关键词作为列名的问题
    cover_url    VARCHAR(255),
    types        TEXT, 
    regions      TEXT, 
    url          VARCHAR(255),
    release_date DATE,
    actor_count  INT,
    vote_count   INT,
    actors       TEXT  
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

5、总程序:确保替换MySQL连接字符串中的username, password, host, 和 database为实际数据库信息。存储到名为movie_rank的Excel文件,并且还会存储到MySQL数据库的movie_rank表中。

import requests
import pandas as pd
import json
from sqlalchemy import create_engine

def fetch_and_extract_movie_data():
    url = "https://movie.douban.com/j/chart/top_list"
    base_params = {
        "type": "5",  # 电影类型
        "interval_id": "100:90",  # 评分区间
        "start": 0,  # 默认从第一个对应开始
        "limit": 100  # 修改为想要获取的电影数量
    }
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36',
    }

    try:
        response = requests.get(url, params=base_params, headers=headers, timeout=10)
        response.raise_for_status()
        data = response.json()
        # 选择特定字段存储
        selected_columns = [
            "id",  # 电影序号
            "title",  # 电影名称
            "score",  # 电影评分
            "rank",  # 电影排名
            "cover_url",  # 电影封面
            "types",  # 电影类型
            "regions",  # 电影地区
            "url",  # 电影链接
            "release_date",  # 电影上映日期
            "actor_count",  # 电影演员数量
            "vote_count",  # 电影评分人数
            "actors",  # 电影演员
        ]
        df = pd.DataFrame(data)[selected_columns]
        # 转换列表为中文JSON字符串
        df['types'] = df['types'].apply(lambda x: json.dumps(x, ensure_ascii=False))
        df['actors'] = df['actors'].apply(lambda x: json.dumps(x, ensure_ascii=False))
        df['regions'] = df['regions'].apply(lambda x: json.dumps(x, ensure_ascii=False))
        return df
    except Exception as e:
        print(f"An error occurred: {e}")
        return pd.DataFrame()

# 存储到excel
def save_to_excel(df, filename):
    df.to_excel(filename, index=False)
    print(f"Data saved to {filename}")

# 存储到mysql
def save_to_mysql(df, db_url, table_name):
    engine = create_engine(db_url)
    df.to_sql(table_name, con=engine, index=False, if_exists='replace', method='multi')
    print(f"Data saved to MySQL table {table_name}")

if __name__ == "__main__":
    df = fetch_and_extract_movie_data()
    if not df.empty:
        save_to_excel(df, 'movie_rank.xlsx')
        db_url = 'mysql+mysqlconnector://username:password@host:port/database'
        save_to_mysql(df, db_url, 'movie_rank')
    else:
        print("No data fetched.")

结果展示:

Excel:

Mysql:

  • 16
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值