import pymysql
import time
import pandas as pd
from lxml import etree
import requests
import re
# 定义一个全局列表变量,用于存放爬取豆瓣电影Top250的信息
allTopMovieList = []
# 封装解析HTML页面和数据爬取的方法
def getParse(url):
# 打印提示信息
print('正在爬取豆瓣电影Top250的信息......')
# 定义一个请求头
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"}
# 构造一个请求对象,并通过get函数发送HTTP请求,最后将响应结果保存于response对象中
html = requests.get(url=url, headers=headers).text
# 解析HTML页面
selector = etree.HTML(html)
# 获取到一部电影的基本信息
lis = selector.xpath('//*[@id="content"]/div/div[1]/ol/li')
for oneSelector in lis:
# 获取豆瓣电影Top250排名
movie_ranking = oneSelector.xpath('./div/div[1]/em/text()')[0]
# 获取豆瓣电影Top250名称
movie_name = oneSelector.xpath('./div/div[2]/div[1]/a/span[1]/text()')[0]
# 获取豆瓣电影Top250的评分
movie_score = oneSelector.xpath('./div/div[2]/div[2]/div/span[2]/text()')[0]
# 获取豆瓣电影Top250的评介人数
movie_number = oneSelector.xpath('./div/div[2]/div[2]/div/span[4]/text()')[0]
movie_number = re.sub(r'D', "", movie_number)
# 获取豆瓣电影Top250的主演和导演
movie_director_starring = oneSelector.xpath('./div/div[2]/div[2]/p[1]/text()')[0]
# python的split()方法:通过指定分隔符对字符串进行切片
# 以Tab键分割movie_director_starring
movie_director_starring_str = movie_director_starring.split(' ')[-1]
movie_director_starring_list = movie_director_starring_str.split(':')
# re.sub(pattern, repl, string, count=0, flags=0):用于替换字符串中的匹配项,返回替换后的值
# 获取豆瓣电影Top250的导演
movie_director = re.sub(r's', ' ', movie_director_starring_list[1]).split('主演')[0]
movie_director = re.sub('&(.*)', '', movie_director)
# 匹配字符串的开头与末尾有空格子字符,替换为空
movie_director = re.sub(' $', '', re.sub('^ ', '', movie_director))
movie_director = re.sub(r'.', '', movie_director)
movie_director = re.sub(' ', '', movie_director)
# 获取豆瓣电影Top250主演
# 判断是否有主演
if len(movie_director_starring_list) == 3:
# 用正则表达式处理电影的主演
movie_starring = re.sub(r'.', '', movie_director_starring_list[-1])
movie_starring = re.sub(r's', ' ', movie_starring) # 's':匹配任何空白字符,包括空格、制表符、换页符等等
movie_starring = re.sub('&(.*)', '', movie_starring)
movie_starring = re.sub(' ', '', movie_starring)
movie_starring = re.sub(''', '', movie_starring)
movie_starring = re.sub(r' ', '', movie_starring)
else:
# 如果没有主演就设置为空
movie_starring = 'null'
# 获取到豆瓣电影Top250的上映年份、制片国家/地区、类型
movie_YCT = oneSelector.xpath('./div/div[2]/div[2]/p[1]/text()')[1]
# 用斜杆来分割上映年份、制片国家/地区、类型
movie_YCT_list = movie_YCT.split('/')
# 获取电影的上映年份 'D':匹配一个非数字字符
movie_releaseDate = re.sub(r'D', '', movie_YCT_list[0])
# 获取豆瓣电影Top250的制片国家 / 地区; 's':匹配任何空白字符,包括空格、制表符、换页符等
movie_country = re.sub(r's', ' ', movie_YCT_list[1])
# 匹配字符串的开头与末尾有空格子字符,替换为空
movie_country = re.sub(' $', '', re.sub('^ ', '', movie_country))
# 获取豆瓣电影Top250的类型
movie_type = re.sub(r's', ' ', movie_YCT_list[-1].split('n')[0]).split(' ')[1:]
# 将列表转换为字符串,其中一'/'来分割字段
movie_type = '/'.join(movie_type)
# append()方法:用于在列表末尾添加新的对象
# 将爬取到的一条电影数据存放到oneMoviesList列表中
oneMoviesList = [
movie_ranking, # 排名
movie_name, # 电影名称
movie_director, # 导演
movie_type, # 电影类型
movie_releaseDate, # 电影上映时间
movie_score, # 电影评分
movie_number, # 批评人数
movie_starring, # 主演
movie_country # 上映国家
]
# 打印每部电影信息
# print(oneMoviesList)
# 再将oneMoviesList列表中的数据存放到allTopMovieList列表中
allTopMovieList.append(oneMoviesList)
def getURLS():
# 定义一个urls列表,用于存放豆瓣电影Top250的url
urls = []
# 观察豆瓣电影Top250的网址如下:
# 第1页:https://movie.douban.com/top250?start=0&filter=
# 第2页:https://movie.douban.com/top250?start=25&filter=
# ..................
# 最后一页:https://movie.douban.com/top250?start=225&filter=
# 则可以总结一下规律:
# 第i页的网址:'https://movie.douban.com/top250?start={}&filter='.format(i)
# 获取每一页的url,并将其存放到urls列表中
for i in range(0, 256, 25):
url = 'https://movie.douban.com/top250?start={}&filter='.format(i)
urls.append(url)
return urls
def saveCSVData(data):
# 将数据保存到.csv文件
data.to_csv("./top250.csv", index=False, mode="w", encoding="utf-8")
def saveJSONData(data):
# 将数据保存到.json文件
data.to_json('./top250.json', orient="records", force_ascii=False)
# 将列表格式的数据转换为Pandas支持的Dataframe
def dataFormatConversion():
# 设定一个列标签
column = [
'movie_rank',
'movie_name',
'movie_director',
'movie_type',
'movie_release_date',
'movie_score',
'movie_number_people',
'movie_starring',
'movie_country'
]
# 调用Dataframe方法
df = pd.Dataframe(allTopMovieList, columns=column)
return df
# 将电影信息保存到MySQL数据库中
def saveMysqlData(user, password, database):
# 连接MySQL服务器
db = pymysql.connect(host="localhost", user=user, password=password, database=database)
# 使用cursor()方法获取操作游标
cursor = db.cursor()
# SQL 插入语句
for one in allTopMovieList:
sql = '''
insert into t_movie(
movie_rank,
movie_name,
movie_director,
movie_type,
movie_release_date,
movie_score,
movie_number_people,
movie_starring,
movie_country
) values (%d,'%s','%s','%s','%s',%f,%d,'%s','%s')
''' % (int(one[0]), one[1], one[2], one[3], one[4], float(one[5]), int(one[6]), one[7], one[8])
try:
# 执行sql语句
cursor.execute(sql)
# 提交到数据库执行
db.commit()
except Exception as e:
# 打印报错信息
print(repr(e))
# 如果发生错误则回滚
db.rollback()
# 关闭数据库连接
db.close()
# 创建存储movie信息的t_movie表
def createMovieTable(user, password, database):
# 打开数据库连接
db = pymysql.connect(host="localhost", user=user, password=password, database=database)
# 使用cursor()方法获取操作游标
cursor = db.cursor()
# 使用 execute() 方法执行 SQL,如果表存在则删除
cursor.execute("DROP TABLE IF EXISTS t_movie")
# 创建t_movie表
sql = '''
create table t_movie(
movie_rank int,
movie_name varchar(255),
movie_director varchar(255),
movie_type varchar(255),
movie_release_date varchar(50),
movie_score float,
movie_number_people int,
movie_starring varchar(255),
movie_country varchar(255)
)character set utf8;
'''
try:
# 执行sql语句
cursor.execute(sql)
# 提交到数据库执行
db.commit()
except Exception as e:
# 打印错误信息
print(repr(e))
# 如果发生错误则回滚
db.rollback()
# 关闭数据库连接
db.close()
if __name__ == '__main__':
# 标记一个开始时间
start_time = time.process_time()
# 获取得豆瓣电影Top250的url列表
urls = getURLS()
print('{:=^50}'.format('开始爬取豆瓣电影信息'))
# 遍历urls列表
for url in urls:
# 调用getParse(url)方法,进行电影信息的爬取
getParse(url)
# 将列表格式的数据转换为Pandas支持的Dataframe
data = dataFormatConversion()
# 将数据保存为csv文件
saveCSVData(data)
# 将数据保存为json文件
saveJSONData(data)
# mysql 用户与密码,连接的mysql的数据库名
user = 'root'
password = 'root'
database = 'movie_db'
# 创建表t_movie,用于存放电影信息
createMovieTable(user, password, database)
# 保存电影信息
saveMysqlData(user, password, database)
# 标记一个结束时间
end_time = time.process_time()
# 打印结束提示信息
print('{:=^50}'.format('爬取结束'))
print("爬取豆瓣电影Top250信息的总时间:{} n总共爬取豆瓣电影Top250信息的条数:{}".format(end_time - start_time, len(allTopMovieList)))