ADD file via upload

main
pwycnofz4 7 months ago
parent f89c39db89
commit 706570807f

@ -0,0 +1,238 @@
from bs4 import BeautifulSoup # 网页解析,获取数据
import re # 正则表达式,进行文字匹配`
import urllib.request, urllib.error # 制定URL获取网页数据
import xlwt # 进行excel操作
import sqlite3 # 进行SQLite数据库操作
from concurrent.futures import ThreadPoolExecutor #优化使用了异步IO和线程池可以提高程序的并发处理能力和性能。
import asyncio #使用了异步IO和线程池可以提高程序的并发处理能力和性能
import matplotlib.pyplot as plt
import numpy as np
from tqdm import tqdm
findLink = re.compile(r'<a href="(.*?)">') # 创建正则表达式对象,标售规则 影片详情链接的规则
findImgSrc = re.compile(r'<img.*src="(.*?)"', re.S)
findTitle = re.compile(r'<span class="title">(.*)</span>')
findRating = re.compile(r'<span class="rating_num" property="v:average">(.*)</span>')
findJudge = re.compile(r'<span>(\d*)人评价</span>')
findInq = re.compile(r'<span class="inq">(.*)</span>')
findBd = re.compile(r'<p class="">(.*?)</p>', re.S)
# 得到指定一个URL的网页内容,反爬手段
def askURL(url):
head = { # 模拟浏览器头部信息,向豆瓣服务器发送消息
"User-Agent": "Mozilla / 5.0(Windows NT 10.0; Win64; x64) AppleWebKit / 537.36(KHTML, like Gecko) Chrome / 80.0.3987.122 Safari / 537.36"
}
request = urllib.request.Request(url, headers=head)
html = ""
try:
# 用户代理,表示告诉豆瓣服务器,我们是什么类型的机器、浏览器(本质上是告诉浏览器,我们可以接收什么水平的文件内容)
response = urllib.request.urlopen(request)
html = response.read().decode("utf-8")
except urllib.error.URLError as e:#捕获 URLError 异常,并将其命名为 e
if hasattr(e, "code"): #查异常对象 e 是否包含 code 属性,用于打印错误代码和原因(如果存在)
print(e.code)
if hasattr(e, "reason"): #查异常对象 e 是否包含reason 属性,用于打印错误代码和原因(如果存在)。
print(e.reason)
return html
#获取网页
def parse_page(html):
soup = BeautifulSoup(html, "html.parser") #用于解析 HTML 内容Python 的内置解析器 html.parse
data_list = [] #用来存储爬取的网页信息
for item in soup.find_all('div', class_="item"):# 查找符合要求的字符串
data = []
item = str(item)
link = re.findall(findLink, item)[0] # 通过正则表达式查找
data.append(link)
img_src = re.findall(findImgSrc, item)[0]
data.append(img_src)
titles = re.findall(findTitle, item) #查找名称
if len(titles) == 2:
c_title = titles[0]
data.append(c_title)
o_title = titles[1].replace("/", "") #消除转义字符
data.append(o_title)
else:
data.append(titles[0])
data.append(' ')
rating = re.findall(findRating, item)[0] #查找评分
data.append(rating)
judge_num = re.findall(findJudge, item)[0] #查找评论
data.append(judge_num)
inq = re.findall(findInq, item) #查找概况
if len(inq) != 0:
inq = inq[0].replace("", "")
data.append(inq)
else:
data.append(" ")
bd = re.findall(findBd, item)[0] #查找相关信息
bd = re.sub('<br(\s+)?/>(\s+)?', "", bd)#替换 bd 中的 <br> 标签及其周围的空白字符为空字符串,以删除换行符
bd = re.sub('/', "", bd) #将 bd 中的斜杠 / 替换为空字符串
data.append(bd.strip())
data_list.append(data)
return data_list
#可视化功能
def visualize(ranting):
# 定义评分区间
bins = [7,8.5, 9, 9.5, 10]
# 使用 numpy.histogram() 计算每个评分区间内的电影数量
counts, _ = np.histogram(ranting, bins=bins)
# 定义每个区间的标签
labels = ['7-8.5', '8.5-9', '9-9.5', '9.5-10']
# 绘制饼图
plt.figure(figsize=(8, 8)) #创建一个图形窗口,设置其大小为 8x8 英寸
#绘制饼图。counts 是各个评分的数量labels 是评分的标签autopct='%1.1f%%' 设置显示百分比,并保留一位小数,
# startangle=140 设置起始角度为 140 度。
plt.pie(counts, labels=labels, autopct='%1.1f%%', startangle=140)
plt.axis('equal') # 保证饼图是圆形而不是椭圆形
plt.title('Distribution of Movie Ratings') #设置图形的标题
plt.tight_layout() #自动调整子图参数,确保图形布局美观。
plt.show()
# 保存数据到表格
def saveData(datalist, savepath):
print("Saving data...")
book = xlwt.Workbook(encoding="utf-8", style_compression=0) #创建一个新的 Excel 工作簿
sheet = book.add_sheet('豆瓣电影Top250', cell_overwrite_ok=True) #来添加一个新的工作表到工作簿中
col = ["电影详情链接", "图片链接", "影片中文名", "影片外国名", "评分", "评价数", "概况", "导演", "主演", "年份", "国家", "类型"]
# 写入列名
for i, column_name in enumerate(col):
sheet.write(0, i, column_name) #向特定的行和列写入数据
# 写入数据
for row_index, movie_info in enumerate(datalist, start=1):#start=1意味着索引从 1 开始,这样可以避免覆盖 Excel 表格的第一行
for col_index, info in enumerate(movie_info):
if isinstance(info, list): # 检查 info 是否是一个列表
for sub_index, sub_info in enumerate(info):
sheet.write(row_index, col_index + sub_index, sub_info) #确保子信息不会覆盖主信息,因为子列表中的内容将从当前列开始逐个写入,按序排列
else:
sheet.write(row_index, col_index, info)
book.save(savepath)
#保存到数据库,创建数据库
def init_db(dbpath):
sql = '''
create table movie250(
id integer primary key autoincrement,
info_link text,
pic_link text,
cname varchar,
ename varchar ,
score numeric,
rated numeric,
instroduction text,
info text
)
''' #创建数据表
conn = sqlite3.connect(dbpath)
cursor = conn.cursor()
cursor.execute(sql)
conn.commit()
conn.close()
#将数据保存到数据库
def saveData2DB(datalist,dbpath):
init_db(dbpath)
conn = sqlite3.connect(dbpath) #函数来连接到 SQLite 数据库文件
cur = conn.cursor() #创建一个游标对象。游标对象用于执行 SQL 查询和获取结果。
for data in datalist:
for index in range(len(data)):
if index == 4 or index == 5: #跳过该索引位置,因为在数据库表格中并不需要这些信息。
continue
data[index] = '"'+data[index]+'"'
sql = '''
insert into movie250(
info_link,pic_link,cname,ename,score,rated,instroduction,info)
values (%s)'''%",".join(data) #将列表 data 中的值通过逗号连接为一个字符串
#print(sql) #输出查询语句,用来测试
cur.execute(sql) #execute() 方法来执行 SQL 插入
conn.commit()
cur.close #关闭数据库
conn.close()
#查询数据库中的数据查询数据库中的电影大于9分的电影
def search():
def execute_query(query, db_path):
conn = sqlite3.connect(db_path) #连接数据库
cursor = conn.cursor() #连接对象的 cursor() 方法来创建一个游标对象。游标对象用于执行 SQL 查询和获取结果
cursor.execute(query) #execute() 方法来执行 SQL 查询
rows = cursor.fetchall() #SELECT 查询使用游标对象的fetchall()方法来获取查询结果
conn.close()
return rows
# 示例查询函数的使用
query = "SELECT * FROM movie250 WHERE score > 9.0"
db_path = "movie.db"
result = execute_query(query, db_path)
for row in result:
print(row)
async def main():
baseurl = "https://movie.douban.com/top250?start=" # 要爬取的网页链接
urls = [baseurl + str(i) for i in range(0, 250, 25)] # 构建URL列表
data=[0]*250
data=await main_async(urls) # 调用异步主函数获取数据
datas = [item for sublist in data for item in sublist] # 使用 flatten() 方法将二维数组拆分成一维
choice=input('请选择存储方式a:excel表b:数据库')
if choice=='a':
savepath = "豆瓣电影Top250.xls" # 当前目录新建XLS存储进去
# 等待异步操作完成后执行保存操作
await asyncio.sleep(3) # 使用异步等待来等待3秒钟
saveData(datas, savepath) # 使用excel表来保存
elif choice=='b':
dbpath = "movie.db" #当前目录新建数据库,存储进去
# 等待异步操作完成后执行保存操作
await asyncio.sleep(3) # 使用异步等待来等待3秒钟
saveData2DB(datas,dbpath) #使用数据库保存
search() #查询数据库里的数据
else:
print('请选择a或者b')
# 假设这里是异步数据获取的部分,等待异步操作完成
ratings = [float(movie[4]) for movie in datas] #提取电影评分
visualize(ratings) #可视化功能
print("爬取完毕!")
# 这是异步数据获取的函数
async def main_async(urls):
# 使用线程池执行异步任务
with ThreadPoolExecutor(max_workers=5) as executor:
loop = asyncio.get_event_loop()
# 将每个 URL 的请求任务加入到事件循环中
tasks = [loop.run_in_executor(executor, askURL, url) for url in urls]
# 等待所有请求任务完成
htmls = await asyncio.gather(*tasks)
# 解析每个页面的 HTML 内容
data = [parse_page(html) for html in htmls]
# 使用tqdm动画效果显示爬取进度
for _ in tqdm(range(len(urls)), desc="正在爬取网页", unit=""):
await asyncio.sleep(0.1) # 模拟爬取网页的耗时
return data
if __name__ == "__main__":
# 运行主函数
asyncio.run(main())
Loading…
Cancel
Save