目标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%的影片
![](https://i-blog.csdnimg.cn/direct/042eb1c153134f3ba39b376c12b04124.png)
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=想要爬取排行前多少的电影![](https://i-blog.csdnimg.cn/direct/452c8060c14e46e586f8912066aa27ec.png)
注意此处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可以接受的格式。像types
和actors
这样的字段是列表类型,在将数据传递到数据库之前,需要将任何列表字段转换成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: