第十周实训

第十周实训——魔王大帝组

该文档已经不是最新,但是可以来方便阅读,有的新内容直接写在了word里(受时间关系导致)

项目概述

我们实现了一个:

  • 上课的基础功能,包括但不限于以下的功能:

    • 第二十条词云图
    • 青岛二手房散点图(面积与价格)
    • 青岛二手房各个地区均价情况
    • 青岛二手房户型分布图
    • 青岛二手房均价最高的几个小
    • 远程代码执行(POC):YOLO人脸识别。
    • 远程代码执行(POC):弹你计算器(字面意思)
  • 获得当前豆瓣热映的电影(根据地区,默认taian(泰安)),然后显示在网页中。

    • 用户可以点击【查看评论】、【查看封面】和【购票】按钮。
    • 【查看评论】会自动爬取前100条评论(接口调用没有写死,可以随便向后申请继续爬取多少条),然后展示给用户。
    • 【购票】从豆瓣获得链接后,自动跳转
    • 【查看封面】查看该电影的封面。(从豆瓣爬取)
    • 【查看评论】会自动根据当前评论通过jieba和wordcloud来生成一张好看的词云图片。(已经将原图的颜色的对比度调整,以便获得更好看的图云图片)
  • 将上述功能转变为API接口,类RESTful,提供功能:

    • 获得当前最热门的电影(同时提供subjectid
    • 根据电影的subjectid(豆瓣提供)来查看评论
    • 获得词云:发送subjectid即可发送回来一个json数据,其中包含base64编码的图片。
    • ai画图:发送subjectid即可。会自动根据当前评论,通过jieba来分词,选取前30条最热门的词汇,调用stable-diffusion XL模型来进行绘画一张符合评论分词的图片。
  • 将上述功能接入qq机器人(通过Graia框架)(但不限于qq机器人,可以通过框架的嵌套同时接入包括但不限于QQ官方机器人、企业微信机器人、钉钉机器人、Kook机器人、discord等)

    • 提供功能:用户发送:/查电影
    • 向用户发送最近热门的电影
    • 用户选择并发送数字(30秒内,如果超时会提示已经超时)
    • 发送电影的基本信息、评论、词云、AI画图的结果。
    • 以上功能的实现封装为了单独的类(同时可以随便封装为包),非常方便各种迁移,以及对于多线程或异步的调度非常方便。
  • SDUST查新闻,包括但不限于,词云,OPENCV数人头,看新闻,数据库存储~

    • 使用了32线程进行快速爬取,可在15分钟内下载3700条以上的新闻全部内容。
    • 异步下载图片,结合多线程可以非常快速地下载数据。
    • 爬虫请求量非常大,15分钟内请求量超过20000+。
    • 支持单线程低调地获取数据。
    • 将有关函数全部封装成模块,可以非常方便地导入和使用。
    • 封装了获取新闻列表、获取新闻页面内容、获取点击量、异步下载图片等功能。
    • 针对不同页面(如泰安校区新闻页)的差异进行了适配。
    • 将新闻内容、点击率、标题等存入sqlite数据库。使用sqlalchemy支持多线程操作。
    • 下载的图片按年月分类保存到本地文件夹。
    • 支持将数据导出为txt、csv等格式,方便其他程序读取使用。
    • 根据新闻内容生成了好看的词云图片。
    • 尝试对网站进行了简单的漏洞扫描,但因限制未完成。
    • 使用sqlite数据库存储爬取的新闻数据,包括新闻标题、发布时间、正文内容、点击量、图片URL等。
    • 引入了sqlalchemy库,封装了数据库操作,支持多线程并发读写,便于迁移到MySQL等其他数据库。
    • 提供了数据插入和查询的简单接口,可以方便地将爬取的数据入库和进行后续分析。
    • 对下载的图片按照年-月的目录结构进行存储归类,便于管理和查看。
    • 支持将数据以JSON、txt、csv等通用格式进行导出,方便与其他语言和系统进行交互和分析。
    • 利用Python的OpenCV库,对爬取的新闻图片进行人脸和人头检测,可以自动统计一张图片中的人数。为进一步分析提供了思路。
    • 对新闻正文内容进行分词处理,并使用wordcloud库生成美观的词云图片,直观展示新闻的热点话题。
    • 尝试使用工具对新闻网站进行了初步的漏洞扫描,但由于对方网站有所限制而未能完成。不过这为网络安全研究提供了一个切入点。
    • 上面的这个数据库,已经接到了flask上
    • 根据上面这个每个新闻可以实时生成词云图
  • YOLO V8 识别人脸和左眼右眼鼻子等定位~

  • docker-compose部署!

    • ovo

主界面

项目部署地址:

因为这个项目一看就全是漏洞,跑在docker容器里,但还是几天重置一次哦。

  1. 部署地址:https://magic-dragon.dayi.ink/ (直连)
  2. 备用部署地址:https://magic-king.dayi.ink/ (cloudflare的负载均衡!防止爆炸)

docker部署

应该是最简单的啦

docker-compose

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FROM python:3.9.19-bullseye
RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
python3-dev \
locales \
tzdata
WORKDIR /app
COPY pythonProject_10_week /app
RUN python3 -m pip install --upgrade pip
RUN pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/

RUN echo "en_US ISO-8859-1\nzh_CN GB2312\nzh_CN.GBK GBK\nzh_CN.UTF-8 UTF-8" > /etc/locale.gen && locale-gen
ENV LANG zh_CN.UTF-8
ENV LANGUAGE zh_CN:zh
ENV LC_ALL zh_CN.UTF-8
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN dpkg-reconfigure -f noninteractive tzdata

RUN echo "export LC_ALL=C" >> ~/.bashrc
CMD ["python3", "/app/ovo_flask/app.py"]

简单build一下

稍微改了一下下

复制文件

当当

AI画图实现细节

SD模型是本地部署的,通过cloudflare隧道接到公网上,用cloudflare的零信任来保证授权,然后用的Stable Diffusion XL模型,根据电影评论,通过jieba分词,来提取前30个最热门的词汇来进行生成提示词(其实还想接入GPT/或者本地AI,来进行生成绘画提示词,但是时间有点不够了)。

具体的实现方法:

其实这个AI绘图其实跟爬虫的类是一个,这样虽然其实是适合分割,但这样放一起可以直接相互调用,方便先实现功能。

  1. 使用get_more_comments方法从豆瓣电影获取指定电影ID的评论数据(可指定获取的评论数量)。

  2. 使用jieba库对获取到的评论文本进行分词,过滤掉一些常见的无意义词汇,统计词频,选取词频最高的前30个词作为关键词。

  3. 使用googletrans库将这些中文关键词翻译成英文。翻译出错时使用原词。

  4. 将翻译后的英文关键词按照一定格式拼接,生成Stable Diffusion的prompt文本,例如:

    1
    "(((best quality))),(((ultra detailed))),(((masterpiece))),illustration," + ",".join(prompt_keywords)

    TAG自动翻译:

  1. 设置Stable Diffusion的inference参数,如negative_prompt,seed,steps,width,height,cfg_scale等。

  2. 向本地部署的Stable Diffusion API接口(https://sdust-sdwebui.0w0.best/sdapi/v1/txt2img 本接口由cloudflare隧道反代,通过cloudflare零信任进行认证客户端授权)发送POST请求,请求参数为第4步生成的prompt文本和第5步设置的其他参数。

    零信任的授权可以直接塞headers里

  3. 接口返回base64编码的图片数据,对其进行解码并返回数据

具体的过程如图:

Stable Diffusion WebUI

Stable Diffusion XL Model

通过豆瓣影评关键词提取,中英互译,自动生成Stable Diffusion的prompt,实现了根据电影评论自动生成相关插画的功能。生成的插画效果好坏很大程度上取决于prompt设计的合理性以及Stable Diffusion模型的生成能力。

不过实际上还不错:

这个是特斯拉大战猩球崛起好像是

txt2img API设置界面

更多的AI图

只是样例啦。

华强买瓜

星际穿越

词云:

熊出没逆转时空

间谍过家家

哈尔滨的移动城堡

黄雀在后

飞驰人生2

QQ机器人实现

使用的graia(多组件组成,主要是AGLP3.0 https://github.com/GraiaProject/ )框架,该框架基于开源项目mirai-http-api(AGLP3.0 https://github.com/project-mirai/mirai-api-http ),然后以插件形式接入mirai(AGLP3.0 https://github.com/mamoe/mirai ) 通过overflow(https://github.com/MrXiaoM/overflow AGLP3.0)然后接入onebot V11(协议)接到LLOneBot。

正因为这个嵌套,反而是每步可以支持了更多的协议,其实完全可以直接接入钉钉、企业微信、kook等。

具体的代码还是看下附录,这里简单写一下。

查询逻辑

/查电影

1
2
3
[无上魔龙大帝]小子!现在版本:1e-05fix 
随机数滴:25235
,无上魔龙大帝正在获得电影列表,不要着急,小子,请输入你想要看的电影数字:

选择查询的电影

1
2
3
4
5
无上魔龙大帝正在给您查询:_13382
电影名称:黄雀在后!
导演:徐伟 何文超
主演:冯绍峰 / 陶虹 / 黄觉
评分:6.3

先发送评论信息

然后调用flask的API发送词云

这个图有精心调过,你会发现其实挺好看的。

最后发送SDXL根据关键词TAG生成出来的图片

比如猩球崛起:

AI画图

AI 画图调用,发送的图实际上都是base64

查询逻辑代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
from modules.jerry_ask.query_movie import dayi_movie_query

@channel.use(
ListenerSchema(
listening_events=[GroupMessage],
decorators=[MatchContent("/查电影")]
)
)
async def ero(app: Ariadne, group: Group, member: Member, message: MessageChain):
recall_msg = []
rd_int = random.randint(1, 114514)
r = await app.send_message(group, MessageChain(f"[无上魔龙大帝]小子!现在版本:{0.00001}fix \n 随机数滴:{rd_int}\n,无上魔龙大帝正在获得电影列表,不要着急,小子,请输入你想要看的电影数字:"))
recall_msg.append(r)

# 创建一个 dayi_movie_query 实例
movie_query = dayi_movie_query()
# 获取电影列表
movie_query.get_list()
movie_info = movie_query.get_movie_info()

# 发送电影列表
pic_base64 = pic_to_base64.text_to_base64(movie_info)
await app.send_group_message(group, Image(base64=pic_base64))

@Waiter.create_using_function([GroupMessage])
async def movie_id_waiter(g: Group, m: Member, msg: MessageChain):
if group.id == g.id and member.id == m.id:
return msg

try:
ret_msg = await inc.wait(movie_id_waiter, timeout=30)
except asyncio.TimeoutError:
r = await app.send_message(group, MessageChain("[无上魔龙大帝查电影]你说话了吗?等待时间超过30秒"))
for i in recall_msg:
try:
await app.recall_message(i)
except:
pass
recall_msg.append(r)
else:
try:
movie_id = int(str(ret_msg.display))
print(movie_id)

await app.send_message(group, MessageChain(f"无上魔龙大帝正在给您查询:_{random.randint(1, 114514)}\n",movie_query.get_name(movie_id)))
# 获取电影评论
movie_query.get_comments(movie_id)
comments = movie_query.get_movie_comments()

# 发送电影评论
pic_base64 = pic_to_base64.text_to_base64(comments)
await app.send_group_message(group, Image(base64=pic_base64))

# 获取电影词云
movie_query.get_wordcloud(movie_id)
#movie_query.get_wordcloud(movie_id)
wordcloud = movie_query.get_movie_wordcloud()
# 发送电影词云
await app.send_group_message(group, Image(base64=wordcloud))

# AI画图 SDXL模型
get_ai_pic = movie_query.get_ai_pic(movie_id)
await app.send_group_message(group, Image(base64=get_ai_pic))

except Exception as e:
error_str = "[dayi-error]不可预料的错误信息:{} 文件:{} 行数{}".format(e, e.__traceback__.tb_frame.f_globals["__file__"], e.__traceback__.tb_lineno)
await app.send_message(group, MessageChain(error_str))

finally:
for i in recall_msg:
try:
await app.recall_message(i)
except:
pass

错误输出

实际运行的时候很难说不出现一点错误,所以还是有错误提示的。

类逻辑

其实看这个小图就可以看明白大概!

把API调用稍微封装一下,然后直接调用python的类就可以实现全部的功能,这样同时可以有助于多线程、异步稳定性,而且封起来可以提升写代码的幸福感哦~

类逻辑:具体的

封装了与电影数据相关的各种操作,包括获取热门电影列表、获取电影评论、生成评论词云以及调用AI绘图接口生成电影相关的图片。通过将这些功能封装在一个类中,我们可以更方便地管理和调用这些功能,同时也提高了代码的可读性和可维护性。

dayi_movie_query类的主要方法及其功能:

  1. get_list(self):

    • 发送GET请求到指定的API接口(http://127.0.0.1:9999/api/get_popular_movie),获取热门电影列表。
    • 解析返回的JSON数据,提取电影信息,并将其格式化为字符串。
    • 将格式化后的电影信息存储在movie_info_str实例变量中。
  2. get_comments(self, movie_id):

    • 根据给定的电影ID,发送GET请求到指定的API接口(http://127.0.0.1:9999/api/get_comments/{movie_id}),获取该电影的评论数据。
    • 解析返回的JSON数据,提取评论信息,并将其格式化为字符串。
    • 将格式化后的评论信息存储在comments实例变量中。
  3. get_wordcloud(self, movie_id):

    • 根据给定的电影ID,发送GET请求到指定的API接口(http://127.0.0.1:9999/api/get_wordcloud/{movie_id}),获取该电影评论的词云数据。
    • 解析返回的JSON数据,将词云数据存储在wordcloud实例变量中。
  4. get_ai_pic(self, movie_id):

    • 根据给定的电影ID,发送GET请求到指定的API接口(http://127.0.0.1:9999/api/ai_draw/{movie_id}),调用AI绘图接口生成与该电影相关的图片。
    • 解析返回的JSON数据,将生成的图片URL存储在ai_pic实例变量中。
  5. get_movie_info(self)get_movie_comments(self)get_movie_wordcloud(self):

    • 这些方法用于获取先前存储在实例变量中的电影信息、评论和词云数据。
  6. get_name(self, movie_id):

    • 根据给定的电影ID,从电影数据中提取电影名称、导演、主演和评分信息,并将其格式化为字符串。
  7. get_movie_data(self, movie_id):

    • 根据给定的电影ID,从电影数据中获取该电影的完整数据。

API接口

概述

此API是基于Flask框架构建的Web服务,用于提供与电影相关的信息,包括当前热映电影列表、电影评论、词云生成以及AI生成图像的功能。API通过调用前面定义的dayi_class_getpop类实例来实现这些功能。

API功能详解

  • 功能:返回当前热映电影的列表。

  • 方法:GET

  • 参数:无

  • 实现:

    • 调用get_now_playing_movies方法获取电影数据。
    • 如果成功,将电影数据返回给客户端;如果发生异常,返回错误代码和错误信息。
  • 请求方法:GET

  • 返回格式:

    • 成功返回:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    {
    "code": "200",
    "data": [
    {
    "title": "电影标题",
    "score": "评分",
    "release_year": "上映年份",
    "duration": "时长",
    "region": "制片地区",
    "director": "导演",
    "actors": ["演员1", "演员2"],
    "category": ["类别1", "类别2"],
    "enough": "是否足够的评分信息",
    "showed": "是否已上映",
    "star": "星级",
    "subject": "电影ID",
    "votecount": "投票数",
    "poster_url": "海报链接",
    "img_url": "图片链接",
    "ticket_url": "购票链接"
    }
    ]
    }
    • 错误返回:
    1
    2
    3
    4
    {
    "code": "400",
    "data": "错误信息描述"
    }

2. 获取电影评论 (/api/get_comments/<int:movie_id>)

  • 功能:根据电影ID返回该电影的前20条评论。

  • 方法:GET

  • 参数:

    • movie_id (int): 电影的唯一标识符。
  • 实现:

    • 调用get_top_20_comments方法,传入电影ID。
    • 如果成功,将评论数据返回给客户端;如果发生异常,返回错误代码和错误信息。
  • 请求方法:GET

  • 返回格式:

    • 成功返回:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "code": "200",
    "data": [
    {
    "review": "评论内容",
    "nickname": "评论者昵称",
    "score": "评分等级",
    "votes": "赞同数",
    "watch_status": "观影状态",
    "comment_time": "评论时间",
    "location": "评论者地点"
    }
    ]
    }
    • 错误返回:
    1
    2
    3
    4
    {
    "code": "400",
    "data": "错误信息描述"
    }

3. 获取词云图片 (/api/get_wordcloud/<int:movie_id>)

  • 功能:根据电影ID生成该电影评论的词云,并以Base64编码的图片形式返回。

  • 方法:GET

  • 参数:

    • movie_id (int): 电影的唯一标识符。
  • 实现:

    • 调用generate_word_cloud方法,传入电影ID,获取词云图片的路径。
    • 将图片文件读取为二进制数据,然后进行Base64编码。
    • 如果成功,将编码后的图片数据返回给客户端;如果发生异常,返回错误代码和错误信息。
  • 请求方法:GET

  • 返回格式:

    • 成功返回:
    1
    2
    3
    4
    {
    "code": "200",
    "data": "Base64编码的图片数据"
    }
    • 错误返回:
    1
    2
    3
    4
    {
    "code": "400",
    "data": "错误信息描述"
    }

4. AI绘图 (/api/ai_draw/<int:movie_id>)

  • 功能:根据电影ID使用AI技术生成相关图像,并返回生成结果。

  • 方法:GET

  • 参数:

    • movie_id (int): 电影的唯一标识符。
  • 实现:

    • 调用dayi_get_ai_pic方法,传入电影ID,获取AI生成的图像数据。
    • 如果成功,将图像数据返回给客户端;如果发生异常,返回错误代码和错误信息。
  • 请求方法:GET

  • 返回格式:

    • 成功返回:
    1
    2
    3
    4
    5
    6
    7
    {
    "code": "200",
    "data": {
    "image_url": "生成的图片链接",
    "other_details": "其他相关信息"
    }
    }
    • 错误返回:
    1
    2
    3
    4
    {
    "code": "400",
    "data": "错误信息描述"
    }

错误处理

  • 在每个API调用中,异常都被捕获并处理,确保在出现问题时能提供明确的反馈给客户端。错误信息包括状态码和异常描述,有助于调试和问题追踪。

安全和性能考虑

  • 安全性:API应确保所有外部输入都进行适当的验证和清理,防止注入攻击等安全风险。这可太多风险了,我有一万种方法把这个服务器干死,有几千种方法注入,有几百种方法直接拿shell。不过,就赌的,你不会来注我OVO。
  • 性能:使用缓存(如在dayi_class_getpop类中实现的)可以提高响应速度并减轻服务器负担。

结论

此API为用户提供了一系列与电影相关的服务,包括获取信息、生成词云和AI图像等。它利用了现代Web技术和数据处理技术,通过清晰的接口设计和有效的异常处理,提供了一个健壮的服务。针对未来的改进,可以考虑进一步增强安全措施并优化性能,以提供更高质量的用户体验。

API接口输出和返回

响应速度:534ms,因为这个接口没有用到缓存,所以我觉得已经超级快啦。

罗小黑战记ovo

2. 获得电影的豆瓣评论 /api/get_comments/<int:movie_id>

1.31s我觉得响应速度还不错,因为后端是新的请求,现爬的。

第二次请求:4ms (这个是有缓存的啦)

3. 获得词云 /api/get_wordcloud/<int:movie_id>

这个接口就想对较慢了,因为要分析生成图片。

13s

也算是可以接受吧,毕竟生成一个图也挺慢的。

图也是没有问题哒

4. SDXL模型画图/api/ai_draw/<int:movie_id>

写文档的时候,山科的网被校外修树的锯断了。

目前没法调用哈哈哈哈哈哈。

证据确凿hhhh 活动: 2024/5/3 10:34:40锯断的。

先来一个错误请求滴吧。要是交报告的时候还是修不好也没法子了,修改目前的网络架构成本巨大。

至少错误返回啦~

普通返回就是data里面套stable diffusion webui返回的数据啦。

补:先租个卡来演示吧

好高级诶

API 接口代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 这里是API啦
@app.route('/api/get_popular_movie')
def ovo_api_get_popu():
data = {
"code": "200",
"data": "ovo?"
}
try:
data['data'] = dayi_parser.get_now_playing_movies()
except Exception as e:
data['data']['code']='400'
data['data']['data']=str(e)

return jsonify(data)

@app.route('/api/get_comments/<int:movie_id>')
def ovo_api_get_comments(movie_id):
data = {
"code": "200",
"data": "ovo?"
}
try:
data['data'] = dayi_parser.get_top_20_comments(move_id=movie_id)
print(movie_id)
except Exception as e:
data['data']['code'] = '400'
data['data']['data'] = str(e)
return jsonify(data)

@app.route('/api/get_wordcloud/<int:movie_id>')
def ovo_api_get_wordcloud(movie_id):
data = {
"code": "200",
"data": "ovo?"
}
try:
image_path = dayi_parser.generate_word_cloud(movie_id=movie_id)
with open(image_path, "rb") as image_file:
encoded_image = base64.b64encode(image_file.read()).decode('utf-8')
data['data'] = encoded_image
except Exception as e:
data['code'] = '400'
data['data'] = str(e)
return jsonify(data)


@app.route('/api/ai_draw/<int:movie_id>')
def ovo_api_ai(movie_id):
data = {
"code": "200",
"data": "ovo?"
}
try:
image_data_json = dayi_parser.dayi_get_ai_pic(movie_id=movie_id)
data['data'] = image_data_json
except Exception as e:
data['code'] = '400'
data['data'] = str(e)
return jsonify(data)

SDUST查新闻

爬取的这个地方:

接口设计思路

  • 代码 201 表示:正常进行,并且成功
  • 代码 401 表示:目标错误,未获得数据
  • 代码 501 表示:程序错误,未知错误,但是可以尝试忽略错误
  • 代码 202 表示:正常返回,返回值有一个或多个。

小细节

  • 我们使用了fake_ua 来减少被服务器风控的可能性,由于只有单一IP进行访问,仍然存在不少的问题。

  • 线程访问速度过快时,可能导致出现一定的错误,但是由于默认尝试30次,所以并不会有显著的错误,但是仍可以在10分钟内下载完约3700条以上的新闻内容。

  • 我们团队还进行了模块的封装,将有关的函数全部封装成模块,不影响代码直接运行的同时,还可以非常方便导入函数

  • 在封装相关的函数之后,我们可以更轻易的直接使用相关的库。最后的main函数只需要进行多线程的管理和函数的传递即可。

  • 比如泰安校区的新闻页,标题点击量跟其他的表情不一样,我们直接重新进行适配。

  • 对于爬虫来说:建立连接->下载数据->写入文件->保存数据。是非常重复并且占用整个时间段的大多数。我们使用了异步下载,不同的下载内容和获取内容不会相互产生明显的影响,可以进行异步。于是,我们写出了异步下载、获得页面等操作。进而提升整体的速度。

小爬虫功能文件封装

直接封装成一个小包啦!

多线程爬取过程

1
2
3
[101, '[dayi-info]不进行删除数据库']
[201, '[dayi-info]数据库目录:./dayi-db.db']
[201, '[dayi-info]初始化连接成功']

32线程进行爬取,快速获得信息。

会自动爬取所有的新闻。

爬取完成后自动存储到数据库中,同时图片也可以看。

异步图片下载

因为下载图片比较慢,所以这里用了异步图片下载,同时结合多线程,这样整体可以以非常快的速度进行下载数据。

下载速度超级快哦

请求量的截图:

爬取过程之一:

报错示例

[501, "[dayi-error]未知错误,获得页面时失败:'NoneType' object has no attribute 'find' 尝试重试次数:1"]

sdust-news爬虫包详细介绍

init.py

就是纯导入模块啦,因为稍微偷了点懒,具体的模块初始化都在每个文件里。

1
2
3
4
5
6
7
8
9
10
11
12
# 直接写成模块吧

from ovo_flask.data.news_sdust.get_list_ywcz import *
from ovo_flask.data.news_sdust.get_pages import *
from ovo_flask.data.news_sdust.download_pic import *
# from get_title_ta import * 没有封装成函数
from ovo_flask.data.news_sdust.get_title import *



if __name__ == '__main__':
pass

download_pic.py

一个异步下载啦,然后失败会自动重试4次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 用于异步下载并保存文件
# 函数调用:
"""
get_and_download(url,file_save_path,file_name)
url : 下载链接
file_save_path : 文件下载目录
file_name : 文件名
"""
# 接口返回: [201,'[dayi-info]图片下载完成:{}{}'.format(file_save_path,file_name)]
import aiohttp
import asyncio
import os

from fake_useragent import UserAgent #pip install fake-useragent
ua = UserAgent() #生成UA

async def async_get_and_save_pic(url,file_save_path,file_name):

为了更好的排版:具体的代码请见附录!

图片下载示例:

获得列表get_list_ywcz.py

也就是获得新闻列表,同时也解析了具体的格式,格式化为list
类似这样:
# 数据格式: [[202,3,title_str,date_datetime,url_str]]
# 数据返回: [[202, 3, '学校获评全国“ 四星易班工作站”', datetime.datetime(2022, 12, 6, 0, 0), 'http://www.sdust.edu.cn/info/1034/15628.htm'],[202,3,'balabal',...]]

所以是很方便用滴,后面插入数据库都很方便。

获得点击量get_title.py

1
2
3
4
#获得标题,时间,点击量(主要是点击量)
#接口调用: get_title_on_news(url)
#接口返回: [202,3,title.get_text(),publish_time_dt,publish_click_num]
#接口示例: [202, 3, '校党委理论学习中心组开展2022年第十九次集体学习', datetime.datetime(2022, 12, 16, 0, 0), '844']

获得页面内容 get_pages.py

因为解析出来的URL有些时候是相对引用,于是就有了fix_url 来自动补全,使用了urllib库文件。然后BS4匹配class,然后直接读取数据即可。抓包可以找到获得点击量的网址。稍微麻烦点的是

1
2
3
4
5
res_num = re.findall(r"\d+\.?\d*",publish_click) 
#匹配 _showDynClicks("wbnews", 1470981840, 15717) 中的两个数字

这样就可以直接解析出 两个数字,并且返回为list类型
publish_time_ls = re.findall(r"\d+\.?\d*",publish_time.get_text())

同样的方法,可以把 发布于:2024-xx-xx
的数字解析出来。

因为整个过程用了datetime来传递时间,这样就可以方便的返回datetime类型

也会去解析图片!

有两个接口,虽然这些接口都是list,也用了异步写了一次,但是为了多线程稳定这里还是用了同步的方法。

1
2
3
4
5
6
7
8
#获得页面的数据
#感谢:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/
#接口说明: 仅获取内容信息,因为部分内容还没有写,也就是说,目前部分只可以获得纯文字内容
#接口1调用: def get_pages_only_text(url:str): url 为网址内容 (返回的只有文字内容)
#接口1返回: return [202,2,'text',news_only_text] (范围的类型如下)
#接口2调用: def get_pages_pic(url:str):
#接口2返回: [[202, 2, 'pic', 'https://ta.sdust.edu.cn/__local/2/D9/3D/B62E635229FAD159BD3B74F8CA6_EFF684AB_2BB65.jpg', 'https://ta.sdust.edu.cn/info/1025/36630.htm']]
#如果接口返回502,说明出现了问题

这里是效果哦,你看图片都有啦!

大一统

也就是把上面所有文件调用一下!

服务器上跑!

效率:在15分钟内可以下载3700条以上的全部内容,媒体内容大概是2.15G左右

当然支持单线程了

当然,我们的代码也支持单线程稳定而低调的获取

数据库

其实挺简单的,就是建表,然后插入数据,插入数据之前预处理一下,有些时候需要返回一下uuid。

后来引用了sqlalchemy,支持了多线程,封装成了两个接口一模一样的包,这样就可以保证之前写的代码的通用性。

使用的是对于小数据集友好的sqlite数据库。因为用了sqlalchemy,实际上非常方便迁移到mysql等其他的数据库

在简单的编写后,封装的class支持以下操作:

  • 支持使用 with 关键字使用class
  • 自动初始化连接,仅需要定义一个dayi_db_ovo() 既可以自动建立连接。
  • 自动建表
  • 执行自定义sql命令
  • 函数插入图片。传递url和日期信息,自动生成uuid,图片基本信息插入数据库,生成待下载目录,返回uuid,等待图片下载
  • 插入文章内容信息,标题,点击率

数据库内容:

新闻内容

图片对应索引

具体的

我们把文件规整起来整理

文章,点击率,标题,都存到我们的数据库中。

使用起来也非常方便,可以很容易的进行导出,也很好使用其他程序进行读取,并且,这么多内容10多M 心动不心动? 而且,随便点开一个,换行符什么的都保留啦,整理的内容好看又好用

图片们

已经按年月分类了捏

里面的图片也非常全!

OPENCV数人头

人这么多,能干什么捏?

要不来数一数有多少人吧?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16个人

嗯..自己数也太麻烦了,要不让AI来干活吧?
于是,便有了AI数数!

于是,我们利用OPENCV 对人头数进行了统计

识别信息,然后返回,最后保存成文件

得出了人头的结论

人头的结论!

好看的词云

引用了一个效果不错的词云,因为是真的好看!!
然后手动加了点stopwords,把数据库导出txt,csv之类的,数据集喂进去效果还是不错的。

他们的原图是这样滴!

SDUST漏洞扫描

我们还想进行简单的漏洞(信息)扫描

但是发现服务器禁止了nmap扫描就放弃了

SDUST查新闻的内容【接到了flask】

新闻内容

可以实时显示数据库

可以翻页ovo,因为内容太多啦。

新闻内容精简版

内容那么长干嘛,简单一点!

当当

我想看图!

这里可以看数据库中的图片ovo

每一页都是可以看哒!

是这样啦

SDUST图云生成

每个新闻都可以生成图云啦

真的蛮辛苦的

我恨JS,但是JS也挺好使救命。

调出来啦~

正在生成中~

我还加了一个正在生成中ovo

当当,看结果吧

生成图

生成完成!

哪个都是可以点滴

真滴

代码细节:简单说

H5实现

就是来个界面,然后生成一个进度条,然后后端传过来数据之后就解析下json,然后显示在前端即可。

后端的实现

查数据库,然后生成图

然后调用那个库文件,也就是那个类(什么活都交给它了哈哈哈哈)

然后简单生成一下就好啦

代码细节:具体来说

本分析主要针对提供的 Flask 模板,该模板包括 HTML 结构、CSS 链接、JavaScript 脚本以及 Flask 模板引擎的语法。该模板主要用于展示内容列表,并提供一个功能用于生成与显示词云。

HTML 结构

  • 头部 (head): 引入了 Bootstrap CSS 和 jQuery,用于页面样式和动态功能。
  • 主体 (body):
    • 容器 (div.container): 包含了页面的主要内容,如标题、表格、分页导航等。
    • 模态框 (div.modal): 用于显示词云生成的进度和结果。
    • 表格 (table): 显示内容列表,包括各种属性如标题、发布时间、点击量等。
    • 分页导航 (nav.pagination): 用于在不同页之间导航。

CSS

  • 使用了 Bootstrap 的样式表来美化页面元素,如表格、按钮和模态框。

JavaScript 详情

概览

JavaScript 代码主要使用 jQuery 库来操纵 DOM 元素,处理用户交互,如点击按钮生成词云。

细节分析
  1. 词云生成按钮的事件绑定:

    • 使用 .generate-wordcloud 类选择器绑定点击事件到所有生成词云的按钮。
    • 当按钮被点击时,从按钮的 data-id 属性获取内容 ID,并触发一系列动作。
  2. 模态框显示与进度条:

    • 显示模态框 (#wordcloudModal).
    • 初始化并动态更新进度条和倒计时,假设生成词云的总时间为 20 秒。
  3. AJAX 请求:

    • 发送异步请求到 /generate_wordcloud/ 加上内容 ID 的 URL,以请求生成词云。
    • 请求成功后,解析响应并更新模态框中的图像源 (img#wordCloudImage)。
    • 处理请求失败的情况,显示错误信息。
  4. 计时与反馈:

    • 设定倒计时结束后的反馈消息。
    • 在 AJAX 请求完成后,更新模态框中的标题和消息,以反映操作的最终状态。
异步操作和错误处理
  • 对 AJAX 请求的错误进行处理,包括请求失败和服务器错误的情况。
  • 使用回调函数来处理请求成功和失败后的 DOM 更新。

Flask 模板引擎

  • 使用 Jinja2 模板语法来动态生成 HTML 内容。
  • {% for content in content_list %}: 循环遍历 content_list,在表格中为每项内容生成一行。
  • {% if %}{% else %}: 条件语句用于处理内容是否存在链接的情况。
  • 分页逻辑的实现,通过 {% for page in range(1, total_pages + 1) %} 迭代生成分页链接。

Flask 模板提供了一个用户界面,用于展示内容列表和通过词云可视化这些内容。它整合了 Bootstrap、jQuery 和 Flask 模板语法来实现一个响应式且交互性强的网页应用。JavaScript 脚本中包含了对用户操作的动态响应,包括通过 AJAX 请求生成词云,并在页面上实时显示进度和结果。

OPENCV看人头

【POC】弹计算器

Oppos.

因为用的subprocess,所以能弹好几个不带冲突的。

YOLO 识别

先看效果啦~

物品:

人物:

人脸:

pytorch纯CPU跑的。

再来点图:




唱歌的图:

YOLO安装

其实超级简单,复制上就可以啦

模型

用的这个模型,来识别的人脸。

https://github.com/derronqi/yolov8-face

下载链接:https://drive.google.com/file/d/1qcr9DbgsX3ryrz2uU8w4Xm3cOrRywXqb/view?pli=1

flask强制缝合怪代码

flask路由代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@app.route("/face_start")
def face_start():
from ultralytics import YOLO
import cv2
model = YOLO('yolov8n.pt')
cap = cv2.VideoCapture(1)
while True:
ret, frame = cap.read()
if not ret:
break
results = model(frame)
annotated_frame = results[0].plot()
cv2.imshow('YOLOv8', annotated_frame)
if cv2.waitKey(1) == ord('q'): # 按下q退出
break

cap.release()
cv2.destroyAllWindows()
return "人脸识别"

HTML代码

1
2
3
<li class="layui-nav-item layui-nav-itemed">
<a target="index" class="" href="/face_start">远程代码执行(POC):YOLO</a>
</li>

执行效果:点击链接后可以正常弹计算器:

执行效果:下载模型后弹出计算器

执行效果:退出后返回人脸识别。

豆瓣热看

其实就是把相关文件整合了一下下,然后稍微通用了一点,这样就不是静态的而是动态的啦。动态生成一些内容,但是由于时间限制还是有待改进,但是我觉得已经可以用啦。

豆瓣有网页可以看最热门的几个视频。

缓存机制

我也不知道为什么我想先说下这个缓存。改成了动态获取评论,因此,评论内容其实就是实时爬下来的。但是后面写代码的时候,发现,好多好多都会重复调用(比如词云,ai绘图..但是爬取过程实际上是不快的,于是便有了直接缓存函数返回值)

接着聊聊这个缓存机制叭~

你看,如果每次有人访问页面,程序都得重新爬一遍评论,这效率可就有点捉急了。所以我就想着,要不给它整个缓存?

于是乎,我就祭出了 Python 的三大缓存装饰器:lru_cache, cached_property 和 cache。它们可以帮我把一些常用的数据缓存下来,下回再调用的时候就可以直接从内存里拿,省了不少事儿~
比如说,像获取电影标题、获取评论这种函数,我就给它们都加上了 @lru_cache 的装饰。这样一来,同样的请求就不会重复爬取了,速度提升了一大截!
当然啦,缓存虽好,也不能滥用。像词云图片的生成,我就只缓存了10个。要不然占用的内存就太大了,得不偿失啊。

总之呢,这个缓存机制就是我对程序的一个小优化啦。虽然不起眼,但是细节决定成败嘛,网页的响应速度快了不少。

其实我还想把这个lru的cache改写为定时自动清理自己的缓存,但是稍微有点复杂,实现了一次发现有点不太稳定,时间也比较赶,就匆匆略过了。

因此这个更新时间其实也是为了检测cache有没有生效。

更新时间的实现逻辑也很简单,一个dict就行。

什么,你问我dict要不要缓存?你猜。

热映电影

最后的效果大概是这样的一个界面:

你还可以固定信息哦~

但是内容要手动自己写哦~这里还可以根据subjectid来获得电影的东东。(但是再写就成了豆瓣客户端了hhh)

返回的dict这个样子哦

海报

对于海报的获得是拿的这个:

但是拿到链接后,要么要本地缓存,要么修一下防盗链即可。


发现这样改会被拒绝。

考虑到本地缓存不一定有版权,所以干脆修防盗链,跳转过去,实际上这个图片是可以直接访问的,所以,直接改成noref也可以滴。

购票功能

其实就是爬的购买的信息的猫眼电影的跳转。但后来发现这个接口不一定是稳定的,有时候豆瓣自己会撤掉,所以不一定真的完美可以用。今天测试还算稳定。

但大部分还是是可以进行购票滴

查看评论

说到获取评论那可是这个豆瓣热看小玩意儿的核心功能之一啊!没有了评论,这电影榜单还有啥看头?所以我在这块儿下了不少功夫。

首先呢,我在 dayi_class_getpop 类里整了一个 get_more_comments() 方法,专门用来获取更多评论。你别看这方法就几行代码,但可厉害了!它通过循环调用 _get_top_20_comments() 方法,每次获取20条评论,最后把所有评论一股脑儿合并到一个列表里,想要多少有多少!

然后呢,在 /comments/<int:movie_id> 路由里,我先调用 get_top_20_comments() 方法获取该电影的前60条评论。为啥是60条?因为在这个方法里面,我偷偷摸摸地又调用了 get_more_comments() 方法,传了个3进去,也就是要获取前3*20=60条评论,嘿嘿~

接着,我又调用 get_update_comments_time() 获取评论的更新时间,再用 get_movie_title() 拿到电影标题。这些数据都整合好了,再丢给评论页的模板,一个漂亮的评论列表就呼之欲出了!

哦对了,光有评论列表多没意思啊,所以我还加了个词云图,贼拉酷炫!调用 generate_wordcloud() 方法,传入电影ID,就能自动生成一张由评论关键词组成的炫酷词云图。不过说到这儿,我发现个小问题:这个方法里我只用了前20条评论,是不是有点浪费?都获取60条评论了,不如全都用上,让词云图更全面、更闪亮!回头我得改改。

还有啊,60条评论一股脑儿都堆页面上,是不是有点儿太多了?万一把用户看晕了咋整?我寻思着,不如整个分页功能,每页20条评论,用户爱看哪页点哪页,这样体验更棒。不过目前还没加,看看小仙女们的评论也不错hhh。

说起这个 get_update_comments_time() 方法,我在里面用了个字典来保存每部电影的评论更新时间,还挺机智的吧?不过话说回来,如果电影多了,这字典会不会占地儿太多?我得想个法子,定期清理一下,把那些老掉牙的电影时间记录给删删,省得占地儿。

这段获取评论的代码已经算是比较完善了,该有的功能都有了,还有词云图这样的亮点。但刚才提到的那些小问题,我回头再好好琢磨琢磨,看看还能不能再优化一下。嗯,就先说到这儿吧!

具体的图片大概这样:

从任何一个电影点进来。

点进去后有评论内容:

看到最后词云也差不多可以生成完毕,就可以看啦

代码细节

这里是app的逻辑,也就是根据具体的电影id(也就是豆瓣的subject_id)来进行调用。

更新时间的逻辑前文已经讲过,存一个dict即可。

获得评论的逻辑(有逻辑嵌套,一次获得20条评论):


然后套到这里

评论就是一个普通爬虫,连UA都不伪装的那种(考虑到评论不是实时更新的,这里的有缓存)

另外由于原爬虫不能获得到标题,这里又写了一小部分

词云功能

美观调整:

把底图稍微调整了一下,调了下对比度,然后把图片的旁边截断:

然后根据背景颜色染了下色。

效果真的我觉得超级好看ovo

大概有这个效果:

这是罗小黑,小黑~(不是哪吒)

小黑~

代码实现细节

跟课上一样啦:获得词->jieba分词->统计词频->塞进去->染色

不过稍微调整了一下,让图片生成出来好看一点。本来打算这里异步处理,后来发现请求不太影响效率。

生成完毕之后保存到静态文件库里

然后返回文件名,flask处理一下静态文件返回就可以了。

再来一张图:

小模块类和日志输出

小模块的实现

这是一个类啦,本来是打算写类的相互继承,来进行写一个大的东东,但是后来发现调试爬虫的时间有点占精力,于是就先写一起。先实现功能再规范,我觉得是一个很好的事情。特别是这种小项目

这里初始化可以把header传过来,可以结合fake_useragent来生成实例。

唯一的缺点是没有写释放函数,不过感觉无关紧要,这里跟数据库的驱动不同。我看着跟数据库的驱动比起来的话,好像也没有什么特别需要添加的内容。也没有什么东西需要手动释放。

然后就是几个函数。

日志们

其实flask基本上就只做了路由,剩下的都是这个小插件的类来实现的,有谁不喜欢日志呢ovo

看看这个日志还是很喜欢滴吧?

对于这个类有专门的模块日志输出哦

还有小模块的日志,独立输出出来

这样实际上flask的app一个类就可以完成所有滴事情~

小模块的导入

很方便很简单哦~

具体的模块作用:

代码定义了一个名为 dayi_class_getpop 的类,提供了多个方法来处理与电影相关的数据和生成相关内容。

详细分析如下:

初始化方法 (init)
  • 功能: 初始化类实例,配置HTTP请求头,设置日志记录。
  • 实现: 使用 requests 库来发送HTTP请求,使用 logging 库进行日志记录。
获取当前热映电影 (get_now_playing_movies)
  • 功能: 获取指定地点的当前热映电影列表。
  • 实现: 发送GET请求到豆瓣电影的热映页面,解析HTML内容以提取电影信息。
获取电影评论 (get_top_20_comments, _get_top_20_comments, get_more_comments)
  • 功能: 获取指定电影的前20条评论或更多。
  • 实现: 使用 requests 发送请求到豆瓣电影的评论页,通过解析HTML内容提取评论数据。利用 lru_cache 提供缓存功能,减少重复请求。
生成词云 (generate_word_cloud)
  • 功能: 根据电影评论生成词云。
  • 实现: 利用 jieba 进行中文分词,使用 wordcloud 库生成词云图像。
翻译和生成AI图片 (dayi_get_ai_pic)
  • 功能: 利用评论生成与电影相关的AI图片。
  • 实现: 使用 googletrans 进行评论关键词翻译,构造图片生成请求发送到图像生成API。

日志记录

  • 功能: 记录操作日志,帮助跟踪错误和程序执行情况。
  • 实现: 使用 logging 库配置了文件和控制台日志记录器。

异常处理

  • 功能: 处理网络请求或其他操作中的异常。
  • 实现: 使用 try-except 块捕获异常,保证程序的健壮性。

课上代码

code1 第一个代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 单行注释
'''
# 单行注释
'''
str="我是一只来自北方的狼"
bnum=True
#复赋
com=2+1j
print(type(com))

if type(com)==complex:
print("我是复数来着")

#列表
list1 = [1,3,4,True,com,str]
for item in list1:
print(item,"***********************\n")

list1.append("哈哈哈 我是卖报的小行家")
#删除第一项
del list1[0]
print(list1)
#自增运算法,数据不能跨越类型,可以进行拼接
tub1 = (1,2,3,4,5,6,7)
tub2 = ("a","b","c","d","e","f","g")
#tub = tub1+tub2
#tub1[0]=888
#print(tub)
[(1,"a"), (2,"b"), (3,"c")]

list1 = [ item for item in zip(tub1,tub2)]
print(list1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# 单行注释
'''
# 单行注释
'''
str = "我是一只来自北方的狼"
bnum = True
# 复赋
com = 2 + 1j
print(type(com))

if type(com) == complex:
print("我是复数来着")

# 列表
list1 = [1, 3, 4, True, com, str]
for item in list1:
print(item, "***********************\n")

list1.append("哈哈哈 我是卖报的小行家")
# 删除第一项
del list1[0]
print(list1)
# 自增运算法,数据不能跨越类型,可以进行拼接
tub1 = (1, 2, 3, 4, 5, 6, 7)
tub2 = ("a", "b", "c", "d", "e", "f", "g")
# tub = tub1+tub2
# tub1[0]=888
# print(tub)
[(1, "a"), (2, "b"), (3, "c")]

list1 = [item for item in zip(tub1, tub2)]
print(list1)

list1 = [ item for item in zip(tub1,tub2)]

# 序列 作业
list2 = sorted(list1,key=lambda x:x[0],reverse=False)
print(list2)
#字典 使用对应Java Map类似 属性:属性值
dict1={
"name":"古他那黑暗之神赵四",
"age":20,
"hobby":"亚洲舞王之炸雷"
}

print(list(dict1.keys()))
print(list(dict1.values()))
#集合
lis3 =[{
'name':'尼古拉嘶赵四',
'age':60,
"hobby":"亚洲舞王之炸雷"

},{
"name":"最强最强妖王谢广坤",
"age":62,
"hobby":"作妖",
},{
"name":"迈克尔刘能",
"age":60,
"hobby":"烫头"}
]

import csv
#将数据库写入到表格
#写入字符串 模式 wb写入字节码 ,会覆盖原来的数据
#a 追加模式,念在末尾添加的数据,不会覆盖
#r读入模式 rb读入学节码
with open("infor.csv","w",newline="",encoding="utf-8") as f:
#获取写入器
write = csv.DictWriter(f,fieldnames=["name","age","hobby"])
write.writerows(lis3)

with open("infor.csv","r",encoding="utf-8") as f:
fread = csv.DictReader(f,fieldnames=["name","age","hobby"])
print("*****************************************")
for item in fread:
print(item)

code2 第二个代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def difNum(list1):
for item1 in list1:
for item2 in list1:
if item1 != item2:
list2.append("%d%d" % (item1, item2))

for item3 in list2:
if item3 not in list3:
list3.append(item3)

print(list3)


# 构建一组数,一个数的两位不重复,数之间也不重复
list1 = [1, 2, 3, 4, 6, 7, 8, 8, 9, 9, 5, 3]
list2 = [] # 用来装载中间变量的列表
list3 = [] # 最终去除重复的列表
difNum(sorted(list1))

code3 类继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Person:
name="人类"
age=20

# 初始化函数
# 父类独有的属性子类不能继承
__priMoney=5000
def __init__(self):
self.name = "小明"
self.age = 40

#动态函数
def MyPrint(self):
print(self.name,f"{self.age}了,大龄剩男",)
return "搞钱吧,别想别的,你已经不是充钱的少年了"

#构析函数
def __del__(self):
print("end_Person_class")

class Mother:
def fly(self):
print("会飞翔")

class Child(Person,Mother):
name = "小强"
age = 18
# def __init__(self):
# self.name = "小量"
# self.age = 17
__priMoney = 1000
# 定义类的函数
def SayHello(self):
print("你好,我是{0},我今年{1}岁了".format(self.name, self.age))
def MyPrint(self):
print("我是子类的输出函数")
print(self.name, "{}了,人够男的".format(self.age))
return "愉快吧,别想别的,你已经不是充钱的少年了"

def priv_money(self):
print("私房钱剩余",self.__priMoney)

pass

if __name__=="__main__":
cc = Child()
cc.SayHello()
cc.MyPrint()
cc.fly()
cc.priv_money()

code4 豆瓣爬虫

镜像源

快下但是好像pycharm不是很喜欢。卡卡的

北外(ustc跳转)镜像源:https://mirrors.ustc.edu.cn/pypi/web/simple

或者直接通过pip install requests beautifulsoup4 bs4 -i https://mirrors.ustc.edu.cn/pypi/web/simple安装

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 获取请求,给远程服务器发送信息 获取数据
import requests
# 网页都是标签组成的,标签之间有嵌套,BeautifulSoup把标签层级化,可以通过内部方法,找到标签属性,和文字
from bs4 import BeautifulSoup
import lxml
import time

url = 'https://movie.douban.com/subject/36208094/comments?status=P'

# 浏览器头部伪装
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Host": "movie.douban.com",
"Cookie": "ll='108302'; bid=UNCukvvPqR8; __pk_id.100001.4cf6=ba0528c5b4d17da0.1704161561.; __yadk_uid=NAiBkhl9crL0ghh6yzHiXUPk"
}

# 获取豆瓣浏览器的响应信息
resp = requests.get(url=url, headers=headers)
print(resp.status_code)
# print(resp.text)

# 解析网页源码
bs = BeautifulSoup(resp.content, "lxml")

# 先找到所有的div class='comment-item'
divs = bs.find_all("div", attrs={"class": "comment-item"})
# print(divs)

for item in divs:
# 当前的item是每个昵称下的评论块
review = item.find("span", attrs={"class": "short"}).text
print("*********************************")
print(review)

nickname = item.find("span", attrs={"class": "comment-info"}).find("a").text
# 输出昵称
print(nickname)

score_tag = item.find("span", attrs={"class": "rating"})
if score_tag:
score = score_tag['title']
print(score)
else:
print("No rating found")

code5 爬虫爬多页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

# 获取请求,给远程服务器发送信息 获取数据
import requests
# 网页都是标签组成的,标签之间有嵌套,BeautifulSoup把标签层级化,可以通过内部方法,找到标签属性,和文字
from bs4 import BeautifulSoup
import lxml
import time

# 浏览器头部伪装
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Host": "movie.douban.com",
"Cookie": "ll='108302'; bid=UNCukvvPqR8; __pk_id.100001.4cf6=ba0528c5b4d17da0.1704161561.; __yadk_uid=NAiBkhl9crL0ghh6yzHiXUPk"
}

# 装载所有数据的列表
list = []

for i in range(10):
try:
time.sleep(1)
url = f"https://movie.douban.com/subject/36208094/comments?start={i * 20}&limit=20&status=P&sort=new_score"
# 获取豆瓣浏览器的响应信息
resp = requests.get(url=url, headers=headers)
print(resp.status_code)
# print(resp.text)

# 解析网页源码
bs = BeautifulSoup(resp.content, "lxml")
# 先找到所有的div class='comment-item'
divs = bs.find_all("div", attrs={"class": "comment-item"})
# print(divs)

for item in divs:
dict1 = {}
# 当前的item是每个昵称下的评论块
review = item.find("span", attrs={"class": "short"}).text
print("*********************************")
print(review)

nickname = item.find("span", attrs={"class": "comment-info"}).find("a").text
# 输出昵称
print(nickname)

score_tag = item.find("span", attrs={"class": "rating"})
if score_tag:
score = score_tag['title']
print(score)
else:
score = "No rating found"

dict1['nickname'] = nickname
dict1['review'] = review
dict1['score'] = score
list.append(dict1)

except Exception as e:
print("出现了异常了", e)
# 这条忽略去下一条
continue

import csv

print("总数据是", list)

# 爬取数据,并且将数据写入到第二十条.csv
with open("第二十条.csv", "w", newline="", encoding="utf-8") as f:
# 获取写入器
fwrite = csv.DictWriter(f, fieldnames=["nickname", "review", "score"])
fwrite.writerows(list)

code6 词云1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#pandas做逻辑清洗
import pandas as pd
from wordcloud import WordCloud
#将彩色图片转化为数字,numpy进行矩阵运算
import numpy as np
#Image.open可以将图片导入到内存当中
from PIL import Image

#测试图片导入python内存中
image = np.array(Image.open("image/111.jpg"))
#print(image)

#通过pandas直接读取csv文件
data = pd.read_csv('第二十条.csv', names=['nickname','review','score'])
#将所有的评论放到一个列表
listCom1 = data['review'].tolist()

#可以将词语中的主谓宾提炼出来
import jieba
#切分词云,join(listCom1)将列表转化为字符串
listCom2 = jieba.lcut(",".join(listCom1))
#strWord = "".join(listCom2)
list2 = []
for item in listCom2:
#print(item)
if len(item) > 1: # 只保留长度大于1的词
list2.append(item)

#统计哪些词语出现的频率比较高
import collections
#统计各个词语频率
word_fre = collections.Counter(list2)
print(word_fre)

# 生成词云
wc = WordCloud(
background_color='white',
mask=image,
font_path='font/SourceHanSansHWSC-Bold.otf', # 设置中文字体
max_words=1000, # 最多显示词数
max_font_size=300 # 最大字号
)
wc.generate_from_frequencies(word_fre)
wc.to_file('词云.jpg') # 保存词云图片

code7 词云2

去掉语气词

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#pandas做逻辑清洗
import pandas as pd
from wordcloud import WordCloud
#将彩色图片转化为数字,numpy进行矩阵运算
import numpy as np
#Image.open可以将图片导入到内存当中
from PIL import Image

#测试图片导入python内存中
image = np.array(Image.open("image/111.jpg"))
#print(image)

#通过pandas直接读取csv文件
data = pd.read_csv('第二十条.csv', names=['nickname','review','score'])
#将所有的评论放到一个列表
listCom1 = data['review'].tolist()

#可以将词语中的主谓宾提炼出来
import jieba
#切分词云,join(listCom1)将列表转化为字符串
listCom2 = jieba.lcut(",".join(listCom1))
#strWord = "".join(listCom2)
list2 = []
for item in listCom2:
#print(item)
if len(item) > 1: # 只保留长度大于1的词
list2.append(item)

#统计哪些词语出现的频率比较高
import collections
#统计各个词语频率

list3= [ ]


filter_word = [',', '的', '。', '了', '是', '我', '很', '在', '', '电影']

for word in list2:
if word in filter_word:
continue
if len(word) > 1:
list3.append(word)
word_fre = collections.Counter(list3)
print(word_fre)

# 生成词云
# 生成词云
wc = WordCloud(
background_color='white',
mask=image,
font_path='font/SourceHanSansHWSC-Bold.otf', # 设置中文字体
max_words=1000, # 最多显示词数
max_font_size=300 # 最大字号
)
wc.generate_from_frequencies(word_fre)
wc.to_file('第二十条词云.jpg') # 保存词云图片

code8 flask

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from flask import Flask, render_template, request

app = Flask(__name__)

# 默认路径的处理函数(路由)
@app.route("/")
def home():
return "<h1 style='color:red'>今天天气有点热,吃不进饭</h1>"

@app.route("/index")
def index():
return render_template('index.html')

# 启动 Flask 服务器
if __name__ == "__main__":
app.run(port=9999, debug=True)

code9 index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
img {
width:600px;
height:500px;
position: absolute;
top:0;
right:0;
bottom:0;
left:0;
margin: auto
}
</style>
</head>
<body>
<img src="../static/img/第二十条词云.jpg"/>
</body>
</html>

code10 复制下文件

压缩包解压拖过去就可以

app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from flask import Flask, render_template, request

app = Flask(__name__)

# 默认路径的处理函数(路由)
@app.route("/")
def home():
return render_template("index.html")

@app.route("/welcome")
def welcome():
return render_template("welcome.html")

@app.route("/wordcloud")
def wordcloud():
# Assuming wordcloud.html is a valid template
return render_template("wordcloud.html")

# 启动 Flask 服务器
if __name__ == "__main__":
app.run(port=9999, debug=True)

fix:

wordcloud_html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="en">


<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
img {
width:600px;
height:500px;
position: absolute;
top:0;
right:0;
bottom:0;
left:0;
margin: auto
}
</style>
</head>
<body>
<img src="../static/img/第二十条词云.jpg"/>
</body>
</html>

code11 散点图

app.py新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from pyecharts.charts import Scatter
import pyecharts.options as opts

@app.route("/scatter")
def scatter():
# 读取 CSV 文件
data = pd.read_csv("static/qingdao.csv",encoding='gbk')

# 删除无用的列
data = data.drop("Unnamed: 0", axis=1)

# 获取面积和对应价格的列表
area_list = data['houseSize'].to_list()
price_list = data['total_price'].to_list()

# 创建散点图
scatter_plot = (
Scatter()
.add_xaxis(xaxis_data=area_list)
.add_yaxis(
series_name="青岛二手房面积价格散点图",
y_axis=price_list,
symbol_size=2,
label_opts=opts.LabelOpts(is_show=False),
)
.set_series_opts()
.set_global_opts(
xaxis_opts=opts.AxisOpts(
type_="value", name="面积/m2", splitline_opts=opts.SplitLineOpts(is_show=True)
),
yaxis_opts=opts.AxisOpts(
type_="value",
name="总价/万",
axistick_opts=opts.AxisTickOpts(is_show=True),
splitline_opts=opts.SplitLineOpts(is_show=True),
),
tooltip_opts=opts.TooltipOpts(is_show=False),
)
.render("templates/scatter.html")
)

return render_template("scatter.html")

散点图

code12 map

新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from pyecharts.charts import Map
@app.route("/map")
def map():
data = pd.read_csv("static/qingdao.csv",encoding='gbk')
data = data.drop("Unnamed: 0", axis=1)
data = data.drop(index=0)

data_grouped = data.groupby('area')['unite_price'].mean().reset_index()
result = [[value['area'], round(value['unite_price'] / 10000, 1)] for index, value in data_grouped.iterrows()]

result[0][0] = '即墨市'
result[1][0] = '城阳区'
result[2][0] = '崂山区'
result[3][0] = '市北区'
result[4][0] = '市南区'
result[5][0] = '平度市'
result[6][0] = '李沧区'
result[7][0] = '胶州市'
result[8][0] = '莱西市'
result[9][0] = '黄岛区'

c = (
Map()
.add("青岛二手房均价", result, "青岛")
.set_global_opts(
title_opts=opts.TitleOpts(title="青岛地图"),
visualmap_opts=opts.VisualMapOpts(min_=0, max_=4, is_piecewise=True),
)
.render("templates/map.html")
)

return render_template("map.html")

Code13 Pie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pyecharts.charts import Pie
@app.route("/pie")
def pie():
data = pd.read_csv("static/qingdao.csv",encoding='gbk')
data = data.drop("Unnamed: 0", axis=1)

temp = data.groupby("houseType")['area'].count().reset_index()
list1 = [(value['houseType'], value['area']) for index, value in temp.iterrows()]

list1 = sorted(list1, key=lambda x: x[1], reverse=True)[:10]

c = (
Pie()
.add("", list1, radius=["30%", "75%"], center=["25%", "50%"], rosetype="radius",
label_opts=opts.LabelOpts(is_show=False))
.set_global_opts(title_opts=opts.TitleOpts(title="房屋类型数量", pos_top=80))
.render("templates/pie.html")
)

return render_template("pie.html")


Code14 bar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from pyecharts.charts import Scatter, Bar
@app.route("/bar")
def bar():
data =pd.read_csv("static/qingdao.csv",encoding="gbk")
#删除无用的列
data=data.drop("Unnamed: 0",axis=1)
#print(data['position'].head(50))
temp = data.groupby("position")["unite_price"].agg(['mean','count']).reset_index()
#print(temp)

result1=[ (value["position"],round(value['mean']/10000,1)) if value['count']>=3 else (0,0) for _,value in temp.iterrows()]
result1=sorted(result1,key=lambda x:x[1],reverse=True)[:10]
#print(result1)
c = (
Bar()
.add_xaxis([ item[0] for item in result1][::-1])
.add_yaxis("二手房均价", [item[1] for item in result1][::-1])
.reversal_axis()
.set_series_opts(label_opts=opts.LabelOpts(position="right"))
.set_global_opts(title_opts=opts.TitleOpts(title="青岛二手房均价最高的几个小区"),
tooltip_opts=opts.TooltipOpts(
formatter="{b}:{c}万元"
)
)
.render("templates/bar.html")
)
return render_template("bar.html")

第十周实训-附录

这里只包括一些核心代码,具体滴请看压缩包啦。

app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
import sqlite3

import pandas as pd
from flask import Flask, render_template, request, send_from_directory,jsonify
from pyecharts.charts import Scatter, Bar
import pyecharts.options as opts
from pyecharts.charts import Map
from pyecharts.charts import Pie
import base64

app = Flask(__name__)


# 默认路径的处理函数(路由)
@app.route("/")
def home():
return render_template("index.html")


@app.route("/welcome")
def welcome():
return render_template("welcome.html")


@app.route("/wordcloud")
def wordcloud():
return render_template("wordcloud.html")


@app.route("/scatter")
def scatter():
# 读取 CSV 文件
data = pd.read_csv("static/qingdao.csv",encoding='gbk')

# 删除无用的列
data = data.drop("Unnamed: 0", axis=1)

# 获取面积和对应价格的列表
area_list = data['houseSize'].to_list()
price_list = data['total_price'].to_list()

# 创建散点图
scatter_plot = (
Scatter()
.add_xaxis(xaxis_data=area_list)
.add_yaxis(
series_name="青岛二手房面积价格散点图",
y_axis=price_list,
symbol_size=2,
label_opts=opts.LabelOpts(is_show=False),
)
.set_series_opts()
.set_global_opts(
xaxis_opts=opts.AxisOpts(
type_="value", name="面积/m2", splitline_opts=opts.SplitLineOpts(is_show=True)
),
yaxis_opts=opts.AxisOpts(
type_="value",
name="总价/万",
axistick_opts=opts.AxisTickOpts(is_show=True),
splitline_opts=opts.SplitLineOpts(is_show=True),
),
tooltip_opts=opts.TooltipOpts(is_show=False),
)
.render("templates/scatter.html")
)

return render_template("scatter.html")


@app.route("/map")
def map():
data = pd.read_csv("static/qingdao.csv",encoding='gbk')
data = data.drop("Unnamed: 0", axis=1)
data = data.drop(index=0)

data_grouped = data.groupby('area')['unite_price'].mean().reset_index()
result = [[value['area'], round(value['unite_price'] / 10000, 1)] for index, value in data_grouped.iterrows()]

result[0][0] = '即墨市'
result[1][0] = '城阳区'
result[2][0] = '崂山区'
result[3][0] = '市北区'
result[4][0] = '市南区'
result[5][0] = '平度市'
result[6][0] = '李沧区'
result[7][0] = '胶州市'
result[8][0] = '莱西市'
result[9][0] = '黄岛区'

c = (
Map()
.add("青岛二手房均价", result, "青岛")
.set_global_opts(
title_opts=opts.TitleOpts(title="青岛地图"),
visualmap_opts=opts.VisualMapOpts(min_=0, max_=4, is_piecewise=True),
)
.render("templates/map.html")
)

return render_template("map.html")


@app.route("/pie")
def pie():
data = pd.read_csv("static/qingdao.csv",encoding='gbk')
data = data.drop("Unnamed: 0", axis=1)

temp = data.groupby("houseType")['area'].count().reset_index()
list1 = [(value['houseType'], value['area']) for index, value in temp.iterrows()]

list1 = sorted(list1, key=lambda x: x[1], reverse=True)[:10]

c = (
Pie()
.add("", list1, radius=["30%", "75%"], center=["25%", "50%"], rosetype="radius",
label_opts=opts.LabelOpts(is_show=False))
.set_global_opts(title_opts=opts.TitleOpts(title="房屋类型数量", pos_top=80))
.render("templates/pie.html")
)

return render_template("pie.html")




@app.route("/bar")
def bar():
data =pd.read_csv("static/qingdao.csv",encoding="gbk")
#删除无用的列
data=data.drop("Unnamed: 0",axis=1)
#print(data['position'].head(50))
temp = data.groupby("position")["unite_price"].agg(['mean','count']).reset_index()
#print(temp)

result1=[ (value["position"],round(value['mean']/10000,1)) if value['count']>=3 else (0,0) for _,value in temp.iterrows()]
result1=sorted(result1,key=lambda x:x[1],reverse=True)[:10]
#print(result1)
c = (
Bar()
.add_xaxis([ item[0] for item in result1][::-1])
.add_yaxis("二手房均价", [item[1] for item in result1][::-1])
.reversal_axis()
.set_series_opts(label_opts=opts.LabelOpts(position="right"))
.set_global_opts(title_opts=opts.TitleOpts(title="青岛二手房均价最高的几个小区"),
tooltip_opts=opts.TooltipOpts(
formatter="{b}:{c}万元"
)
)
.render("templates/bar.html")
)
return render_template("bar.html")


@app.route("/face_start")
def face_start():
org_str="""<pre>@app.route("/face_start")
def face_start():
return "【docker部署】为保证服务器的稳定,这段POC已经被直接return掉,详细可以看app.py的代码OVO。人脸识别"
from ultralytics import YOLO
import cv2
model = YOLO('yolov8n.pt')
cap = cv2.VideoCapture(1)
while True:
ret, frame = cap.read()
if not ret:
break
results = model(frame)
annotated_frame = results[0].plot()
cv2.imshow('YOLOv8', annotated_frame)
if cv2.waitKey(1) == ord('q'): # 按下q退出
break

cap.release()
cv2.destroyAllWindows()
return "人脸识别" </pre>"""

return "【docker部署】为保证服务器的稳定,这段POC已经被直接return掉,详细可以看app.py的代码OVO。人脸识别"+org_str
from ultralytics import YOLO
import cv2
model = YOLO('yolov8n.pt')
cap = cv2.VideoCapture(1)
while True:
ret, frame = cap.read()
if not ret:
break
results = model(frame)
annotated_frame = results[0].plot()
cv2.imshow('YOLOv8', annotated_frame)
if cv2.waitKey(1) == ord('q'): # 按下q退出
break

cap.release()
cv2.destroyAllWindows()
return "人脸识别"






# by dayi

@app.route("/sdust_get_data")
def fsdust_start():
org_str = """<pre>@app.route("/sdust_get_data")
def fsdust_start():
org_str = "org"
return "【docker部署】为保证服务器的稳定,这段POC已经被直接return掉,详细可以看app.py的代码OVO。爬完了" + org_str
from data.test import do_it
do_it()
return "爬完了" </pre>"""
return "【docker部署】为保证服务器的稳定,这段【爬网站】已经被直接return掉,详细可以看app.py的代码OVO。爬完了" + org_str
from data.test import do_it
do_it()
return "爬完了"

@app.route("/dayi_poc_calc")
def tanjisuanqi():
import subprocess
try:
subprocess.Popen(['calc.exe'])
return "你弹了服务器的计算器 !然而这个服务器是Linux 还是容器嘎嘎嘎嘎"
except Exception as e:
return f"【错误】 你弹了服务器的计算器 !然而这个服务器是Linux 还是容器嘎嘎嘎嘎 Error: {e}"


from plugins.get_pop import dayi_class_getpop #这里导入了个类
import datetime
dayi_parser = dayi_class_getpop() #初始化类


@app.route('/pop')
def index_pop():
# now time
time_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
parser = dayi_parser
movies = parser.get_now_playing_movies()
# print(movies)
return render_template('pop_movie_list.html', movies=movies,time_str=time_str)

@app.route('/comments/<int:movie_id>')
def movie_comments(movie_id):
parser = dayi_parser
comments = parser.get_top_20_comments(movie_id)
time_str = parser.get_update_comments_time(movie_id) #更新时间
movie_title = parser.get_movie_title(movie_id) # 获取电影名称
return render_template('movie_comments.html', comments=comments,movie_title=movie_title,time_str=time_str,movie_id=movie_id)


@app.route('/wordcloud/<int:movie_id>')
def generate_wordcloud(movie_id):
parser = dayi_parser
filename = parser.generate_word_cloud(movie_id)
print(filename)
return send_from_directory('static',"/".join(filename.split("/")[1:]))
# app.send_static_file(filename)
# return render_static('wordcloud.html', filename=filename)
# return filename






# 这里是API啦
@app.route('/api/get_popular_movie')
def ovo_api_get_popu():
data = {
"code": "200",
"data": "ovo?"
}
try:
data['data'] = dayi_parser.get_now_playing_movies()
except Exception as e:
data['data']['code']='400'
data['data']['data']=str(e)

return jsonify(data)

@app.route('/api/get_comments/<int:movie_id>')
def ovo_api_get_comments(movie_id):
data = {
"code": "200",
"data": "ovo?"
}
try:
data['data'] = dayi_parser.get_top_20_comments(move_id=movie_id)
print(movie_id)
except Exception as e:
data['data']['code'] = '400'
data['data']['data'] = str(e)
return jsonify(data)

@app.route('/api/get_wordcloud/<int:movie_id>')
def ovo_api_get_wordcloud(movie_id):
data = {
"code": "200",
"data": "ovo?"
}
try:
image_path = dayi_parser.generate_word_cloud(movie_id=movie_id)
with open(image_path, "rb") as image_file:
encoded_image = base64.b64encode(image_file.read()).decode('utf-8')
data['data'] = encoded_image
except Exception as e:
data['code'] = '400'
data['data'] = str(e)
return jsonify(data)


@app.route('/api/ai_draw/<int:movie_id>')
def ovo_api_ai(movie_id):
data = {
"code": "200",
"data": "ovo?"
}
try:
image_data_json = dayi_parser.dayi_get_ai_pic(movie_id=movie_id)
data['data'] = image_data_json
except Exception as e:
data['code'] = '400'
data['data'] = str(e)
return jsonify(data)


#### SDUST

from flask import Flask, render_template, request

@app.route('/sdust_show_database_content')
def indexovoovov():
conn = sqlite3.connect('data/dayi-db.db')
cursor = conn.cursor()

page = request.args.get('page', default=1, type=int)
per_page = 10 # 每页显示的记录数

offset = (page - 1) * per_page
cursor.execute('SELECT * FROM content_list LIMIT ? OFFSET ?', (per_page, offset))
content_list = cursor.fetchall()

cursor.execute('SELECT COUNT(*) FROM content_list')
total_records = cursor.fetchone()[0]
total_pages = (total_records + per_page - 1) // per_page

conn.close()

return render_template('sdust_content.html',
content_list=content_list,
current_page=page,
total_pages=total_pages)


@app.route('/sdust_show_database_content_easy')
def indexovoovov_():
conn = sqlite3.connect('data/dayi-db.db')
cursor = conn.cursor()

page = request.args.get('page', default=1, type=int)
per_page = 10 # 每页显示的记录数

offset = (page - 1) * per_page
cursor.execute('''
SELECT
id,
content_title,
content_publish_time,
content_publish_time_unix,
content_clicks_num,
SUBSTR(content_only_text, 1, 50) AS content_only_text,
content_url,
SUBSTR(content_all, 1, 100) AS content_all,
SUBSTR(json, 1, 100) AS json
FROM content_list
LIMIT ? OFFSET ?
''', (per_page, offset))
content_list = cursor.fetchall()

cursor.execute('SELECT COUNT(*) FROM content_list')
total_records = cursor.fetchone()[0]
total_pages = (total_records + per_page - 1) // per_page

conn.close()

return render_template('sdust_content_easy.html',
content_list=content_list,
current_page=page,
total_pages=total_pages)

@app.route('/sdust_show_media')
def show_media():
conn = sqlite3.connect('data/dayi-db.db')
cursor = conn.cursor()

page = request.args.get('page', default=1, type=int)
per_page = 12 # 每页显示的记录数

offset = (page - 1) * per_page
cursor.execute('SELECT * FROM media_list LIMIT ? OFFSET ?', (per_page, offset))
media_list = cursor.fetchall()

cursor.execute('SELECT COUNT(*) FROM media_list')
total_records = cursor.fetchone()[0]
total_pages = (total_records + per_page - 1) // per_page

conn.close()

return render_template('sdust_media.html',
media_list=media_list,
current_page=page,
total_pages=total_pages)



# import jieba
# from wordcloud import WordCloud
# import random
#


@app.route('/generate_wordcloud/<int:content_id>')
def generate_wordcloud_route(content_id):
conn = sqlite3.connect('data/dayi-db.db')
cursor = conn.cursor()

cursor.execute('SELECT content_only_text FROM content_list WHERE id = ?', (content_id,))
result = cursor.fetchone()

if result:
text = result[0]
res = dayi_parser.sdust_generate_wordcloud(text=text,id=content_id)
return jsonify({'status': 'success', 'url': '/'+res})
else:
return jsonify({'status': 'error', 'message': 'Content not found'})

@app.route('/sdust_rentou')
def sdust_rentou_route():
return render_template("rentou.html")


####


# 启动 Flask 服务器
if __name__ == "__main__":
app.run(port=9999, debug=True)

flask的类get_pop.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# https://movie.douban.com/cinema/nowplaying/beijing/
import collections
import datetime
import json
import random

import jieba
import numpy as np
import requests
from PIL import Image
from bs4 import BeautifulSoup
import lxml
from urllib.request import urlopen
from urllib.request import Request
import logging

from functools import lru_cache, cached_property, cache #warps
import time
from wordcloud import WordCloud, ImageColorGenerator



class dayi_class_getpop:
def __init__(self,headers=None):
self.time_str={}
# self.time_str.setdefault()
self.logger = logging.getLogger('dayi_class_logger')
self.logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

self.file_logger = logging.FileHandler('dayi_class.log')
self.file_logger.setFormatter(formatter)

self.logger.addHandler(self.file_logger)

ch = logging.StreamHandler()
ch.setFormatter(formatter)
self.logger.addHandler(ch)

if headers is None:
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Host": "movie.douban.com",
# "Cookie": "ll='108302'; bid=UNCukvvPqR8; __pk_id.100001.4cf6=ba0528c5b4d17da0.1704161561.; __yadk_uid=NAiBkhl9crL0ghh6yzHiXUPk"
"Cookie":"ll='118225'; bid=wgIh1dhMIjE; _pk_ref.100001.4cf6=%5B%22%22%2C%22%22%2C1714407559%2C%22http%3A%2F%2F127.0.0.1%3A9999%2F%22%5D; _pk_id.100001.4cf6=be6d5238ba848145.1714403730.; ap_v=0,6.0; __utma=30149280.1094153631.1714403731.1714403731.1714407560.2; __utmc=30149280; __utmz=30149280.1714403731.1.1.utmcsr=127.0.0.1:9999|utmccn=(referral)|utmcmd=referral|utmcct=/; __utma=223695111.859443514.1714403731.1714403731.1714407560.2; __utmc=223695111; __utmz=223695111.1714403731.1.1.utmcsr=127.0.0.1:9999|utmccn=(refe…9DD72AD236698917929FFCAB396E6|1d475f651fc8d2868e2e89900a85c4ca; _ga_Y4GN1R87RG=GS1.1.1714407556.1.0.1714407556.0.0.0; _ga=GA1.2.654353471.1714407556; _gid=GA1.2.1665559982.1714407557; _pk_ses.100001.4cf6=1; __utmb=30149280.0.10.1714407560; __utmb=223695111.0.10.1714407560; FCNEC=%5B%5B%22AKsRol88UFf2miudy22FUSE9gaUGrn4nJ38iRfYERJIi_fhlwMYGMRVRmVOpvoXlZaPeMYW14tj2c7gMSUaoid14jDluYZtTl3VVcgie2FFRsSiMt4Pvpctd3AqGjsVe7xVNVVrRSSX9zN8vYJ4gf1dOLZEy7DeRNQ%3D%3D%22%5D%5D; __yadk_uid=VEBXrh6PRAFPoZN7qfimQEPftfDJA5KF".encode("utf-8").decode("latin1")
}
else:
headers=headers

def get_now_playing_movies(self, location="taian"):
self.logger.info("正在获得热映表,城市:"+location)
url = f'https://movie.douban.com/cinema/nowplaying/{location}/'
try:
response = requests.get(url, headers=self.headers)
response.raise_for_status() # 检查响应状态码
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
return []

soup = BeautifulSoup(response.content, "lxml")
movies_ul = soup.find("div", attrs={"id": "nowplaying"}).find("ul", attrs={"class": "lists"})

if not movies_ul:
print("Error: Cannot find the 'lists' ul.")
return []

movies = []
for movie_item in movies_ul.find_all("li", attrs={"class": "list-item"}):
movie_info = {
"title": movie_item.get("data-title"),
"score": movie_item.get("data-score"),
"release_year": movie_item.get("data-release"),
"duration": movie_item.get("data-duration"),
"region": movie_item.get("data-region"),
"director": movie_item.get("data-director"),
"actors": movie_item.get("data-actors"),
"category": movie_item.get("data-category"),
"enough": movie_item.get("data-enough"),
"showed": movie_item.get("data-showed"),
"star": movie_item.get("data-star"),
"subject": movie_item.get("data-subject"),
"votecount": movie_item.get("data-votecount"),
"poster_url": movie_item.find("li", class_="poster").find("a").get("href") if movie_item.find("li",
class_="poster") else None,
"img_url": movie_item.find("li").find("img").get("src") if movie_item.find("li") else None,
"ticket_url": movie_item.find("li", class_="sbtn").find("a").get("href") if movie_item.find("li",
class_="sbtn") else None
}

movies.append(movie_info)

movies.append({
"title": "【固定】罗小黑战记",
"score": None,
"release_year": "2019",
"duration": "101分钟",
"region": "中国大陆",
"director": "木头",
"actors": ["山新", "郝祥海", "刘明月", "曹云图", "叮当", "更多..."],
"category": ["动作", "动画", "奇幻"],
"enough": None,
"showed": None,
"star": None,
"subject": 26709258,#不能为空!
"votecount": None,
"poster_url": None,
"img_url": None,
"ticket_url": "https://luoxiaohei-movie.com"
})

movies.append({
"title": "【固定】星际穿越",
"score": "9.4",
"release_year": "2014",
"duration": "169分钟",
"region": "美国 / 英国 / 加拿大",
"director": "克里斯托弗·诺兰",
"actors": ["马修·麦康纳", "安妮·海瑟薇", "杰西卡·查斯坦", "麦肯吉·弗依", "卡西·阿弗莱克"],
"category": ["剧情", "科幻", "冒险"],
"enough": None,
"showed": None,
"star": None,
"subject": 1889243,
"votecount": "1949584",
"poster_url": None,
"img_url": None,
"ticket_url": None
})

movies.append({
"title": "【固定】肖申克的救赎",
"score": "9.7",
"release_year": "1994",
"duration": "142分钟",
"region": "美国",
"director": "弗兰克·德拉邦特",
"actors": ["蒂姆·罗宾斯", "摩根·弗里曼", "鲍勃·冈顿", "威廉姆·赛德勒", "克兰西·布朗"],
"category": ["剧情", "犯罪"],
"enough": None,
"showed": None,
"star": None,
"subject": 1292052,#不能为空!
"votecount": "3016127",
"poster_url": None,
"img_url": None,
"ticket_url": None
})

return movies

# @lru_cache(maxsize=1)
def get_top_20_comments(self,move_id,start_id=0):
# return self._get_top_20_comments(move_id,start_id)
return self.get_more_comments(move_id,3)

def get_update_comments_time(self,move_id): #move_id 写错了hhhh不改了,将错就错
self.time_str.setdefault(move_id,None)
if self.time_str[move_id]==None:
return "未获得更新时间"
# self.time_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S NO CACHE')
return self.time_str[move_id]


# 获得前20条评论
@lru_cache(maxsize=1024)
def _get_top_20_comments(self, movie_id,start_id = 0):

self.time_str[movie_id] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

self.logger.info("开爬!" + str(movie_id) +"多少条开始:"+ str(start_id*20))
url = f"https://movie.douban.com/subject/{movie_id}/comments?start={start_id * 20}&limit=20&status=P&sort=new_score"
try:
resp = requests.get(url, headers=self.headers)
resp.raise_for_status() # 检查响应状态码
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
return []

soup = BeautifulSoup(resp.content, "lxml")
comments = soup.find_all("div", attrs={"class": "comment-item"})

comments_list = []
for comment in comments:
review = comment.find("span", attrs={"class": "short"}).text.strip()
nickname = comment.find("span", attrs={"class": "comment-info"}).find("a").text.strip()
score_tag = comment.find("span", attrs={"class": "rating"})
score = score_tag["title"] if score_tag else "No rating found"

comment_dict = {
"review": review,
"nickname": nickname,
"score": score,
"votes": comment.find("span", attrs={"class": "comment-vote"}).find("span",
attrs={"class": "votes"}).text,
"watch_status": comment.find("span", attrs={"class": "comment-info"}).find("span",
recursive=False).text,
"comment_time": comment.find("span", attrs={"class": "comment-time"})["title"],
"location": comment.find("span", attrs={"class": "comment-location"}).text if comment.find("span",
attrs={
"class": "comment-location"}) else ""
}
comments_list.append(comment_dict)
return comments_list

@lru_cache(maxsize=10)
def get_movie_title(self, movie_id):
self.logger.info("正在尝试从网页解析标题..." )
url = f"https://movie.douban.com/subject/{movie_id}/"
try:
resp = requests.get(url, headers=self.headers)
resp.raise_for_status() # 检查响应状态码
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
return ""

soup = BeautifulSoup(resp.content, "lxml")
# title = soup.find("h1", attrs={"property": "v:itemreviewed"}).text
title = soup.find("h1").text.replace("\n", " ")
# logger.info("")
self.logger.info("从网页解析标题成功:"+title)
return title

def sdust_generate_wordcloud(self,text, id):
self.logger.info("[sdust]正在生成词云")
rd_int = random.randint(11451, 11451191928008)
words = jieba.cut(text)
background = np.array(Image.open('image/111.jpg'))
filtered_words = [word for word in words if
len(word) > 1 and word not in [',', '的', '。', '了', '是', '我', '很', '在', '', '电影']]
wordcloud = WordCloud(font_path='font/SourceHanSansHWSC-Bold.otf', width=800, height=800,
mask=background,background_color='white',).generate(' '.join(filtered_words))
image_color = ImageColorGenerator(background)
wordcloud.recolor(color_func=image_color)
wordcloud.to_file(f'static/sdust_wordcloud/wordcloud_{id}_{rd_int}.png')
out_str = f'static/sdust_wordcloud/wordcloud_{id}_{rd_int}.png'
return out_str

@lru_cache(maxsize=10)
def generate_word_cloud(self, movie_id):
self.logger.info("正在生成词云")
comments = self.get_top_20_comments(movie_id)
review_texts = [comment['review'] for comment in comments]
# 切分词云,将列表转化为字符串
self.logger.info("正在生成词云_导入jieba库")
words = jieba.lcut(",".join(review_texts))
filtered_words = [word for word in words if
len(word) > 1 and word not in [',', '的', '。', '了', '是', '我', '很', '在', '', '电影']]
# 统计词语频率
word_freq = collections.Counter(filtered_words)
self.logger.info("正在生成词云{}".format(movie_id))
# 生成词云
# mask = np.array(Image.open("image/111.jpg"))
background = np.array(Image.open('image/111.jpg'))
wc = WordCloud(
background_color='white',
mode='RGB',
mask=background,
# mask=backgroud, # 添加蒙版,生成指定形状的词云,并且词云图的颜色可从蒙版里提取
max_words=600,
# stopwords=STOPWORDS, # 内置的屏蔽词,并添加自己设置的词语
# font_path='C:\Windows\Fonts\STZHONGS.ttf',
max_font_size=250,
relative_scaling=0.6, # 设置字体大小与词频的关联程度为0.4
random_state=50,
scale=2,
# background_color='white',
# mask=mask,
font_path='font/SourceHanSansHWSC-Bold.otf', # 设置中文字体
# max_words=1000, # 最多显示词数
# max_font_size=300 # 最大字号
)
image_color = ImageColorGenerator(background) # 设置生成词云的颜色,如去掉这两行则字体为默认颜色
wc.generate_from_frequencies(word_freq)
wc.recolor(color_func=image_color)
self.logger.info("词云生成完毕{}".format(movie_id))
# 保存词云图片
filename = f"static/wordcloud/wordcloud_{movie_id}.jpg"
wc.to_file(filename)
return filename

@lru_cache(maxsize=1024)
def get_more_comments(self,movie_id=36008597,what_more=10):
"""
获得更多的评论信息哦~
:param movie_id: 电影ID
:param what_more: 多少个20
:return: 一个list OVO
"""
res = []
for i in range(what_more):
res = res+ self._get_top_20_comments(movie_id,i)
return res


def dayi_get_ai_pic(self,movie_id=36008597):
self.logger.info("正在词")
comments = self.get_top_20_comments(movie_id)
review_texts = [comment['review'] for comment in comments]
# 切分词云,将列表转化为字符串
self.logger.info("正在生成词云_导入jieba库")
words = jieba.lcut(",".join(review_texts))
filtered_words = [word for word in words if
len(word) > 1 and word not in [',', '的', '。', '了', '是', '我', '很', '在', '', '电影']]
# 统计词语频率
word_freq = collections.Counter(filtered_words)
print(word_freq)

from googletrans import Translator

# 选择词频最高的前20个关键词
top_words = word_freq.most_common(30)

# 创建Translator对象,用于调用谷歌翻译API
translator = Translator()

# 翻译这些关键词
self.logger.info("正在进行词汇翻译")
translated_words = {}
for word in top_words:
try:
translation = translator.translate(word[0], dest='en')
translated_words[word] = translation.text
except Exception as e:
print(f"Error translating '{word}': {e}")
translated_words[word] = word # If translation fails, use the original word
self.logger.error(f"Failed to translate '{word}'. Error: {e}")

# 输出翻译后的关键词
print("Translated words:", translated_words)

# 构建Stable Diffusion的prompt,使用翻译后的英文关键词
prompt_keywords = [translated_words[i] for i in dict(translated_words)]
prompt = "(((best quality))),(((ultra detailed))),(((masterpiece))),illustration," + ",".join(prompt_keywords)
print("prompt:", prompt.lower())

base_url = 'https://sdust-sdwebui.0w0.best/'
url_ = '/sdapi/v1/txt2img'

headers = {'Accept': 'application/json',
'CF-Access-Client-Id': '60eac0fe85c255e79ab60a6d07c6bef3.access',
'CF-Access-Client-Secret': 'fb21b062612f166ae720fd0871748dce3668c79b89ddd1a6c22b8dde328db65a'
}

post_url = 'https://sdust-sdwebui.0w0.best/sdapi/v1/txt2img'
prompt_data = {'prompt': prompt.lower(),
'negative_prompt': '',
'seed': -1,
'steps': 50,
'width': 512,
'height': 800,
'cfg_scale': 7.5
}
# 'sampler_index': 'DPM++ SDE',
resp = requests.post(url=post_url,data=json.dumps(prompt_data),headers=headers)
#data['images'][0]

return resp.json()

# print(resp.json())




if __name__ == '__main__':
print("================Debug mode!!!================")
r = dayi_class_getpop()
# print(r.get_more_comments(36093612,10))
# print(r.get_more_comments(36093612, 10))
# r.generate_word_cloud(36093612)
print(r.dayi_get_ai_pic())

init.py

1
2
3
4
5
6
7
8
9
10
# 直接写成模块吧

from ovo_flask.data.news_sdust.get_list_ywcz import *
from ovo_flask.data.news_sdust.get_pages import *
from ovo_flask.data.news_sdust.download_pic import *
# from get_title_ta import * 没有封装成函数
from ovo_flask.data.news_sdust.get_title import *

if __name__ == '__main__':
pass

download_pic.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 用于异步下载并保存文件
# 函数调用:
"""
get_and_download(url,file_save_path,file_name)
url : 下载链接
file_save_path : 文件下载目录
file_name : 文件名
"""
# 接口返回: [201,'[dayi-info]图片下载完成:{}{}'.format(file_save_path,file_name)]

import aiohttp
import asyncio
import os

from fake_useragent import UserAgent #pip install fake-useragent
ua = UserAgent() #生成UA

async def async_get_and_save_pic(url,file_save_path,file_name):
test_url = url
file_all_path = file_save_path+file_name
headers={'User-Agent':ua.random} #随机生成UA
async with aiohttp.ClientSession(headers=headers) as session:
async with session.get(test_url) as response:
# print("Status:", response.status)
# print("Content-type:", response.headers['content-type'])
if not os.path.isdir(file_save_path):
os.makedirs(file_save_path)
with open(file_all_path, "wb") as f:
data = await response.read()
f.write(data)

def get_and_download(url,file_save_path,file_name):
att = 0
while att<=4:
try:
asyncio.run(async_get_and_save_pic(url,file_save_path=file_save_path,file_name=file_name))
return [201,'[dayi-info]图片下载完成:{}{}'.format(file_save_path,file_name)]
except Exception as e:
att+=1
return [501,'[dayi-error]未知错误:{}'.format(str(e))]
return [503,'[dayi-error]图片下载失败,url:{}'.format(url)]
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

if __name__ == '__main__':
url = 'http://news.sdust.edu.cn/__local/4/71/66/DD61B615A24D9B337A2087857B3_08AB904E_4540E.jpg?e=.jpg'
file_save_path = '.'
file_name = 'test.png'
print(get_and_download(url,file_save_path,file_name))

get_list_ywcz.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# 要闻传真的获取
# 该文件只能获得要闻的数据即 : news.sdust.edu.cn/ywcz/
# 数据格式: [[202,3,title_str,date_datetime,url_str]]
# 数据返回: [[202, 3, '学校获评全国“ 四星易班工作站”', datetime.datetime(2022, 12, 6, 0, 0), 'http://www.sdust.edu.cn/info/1034/15628.htm'],[202,3,'balabal',...]]

import requests #pip install requests
from bs4 import BeautifulSoup #pip install bs4
from fake_useragent import UserAgent #pip install fake-useragent
import re #模糊匹配
import urllib #自动补全网址
import datetime

dayi_debug=1

ua = UserAgent() #生成UA

url = 'http://news.sdust.edu.cn/ywcz.htm'
url_auto = 'http://news.sdust.edu.cn/' #补全用的

def get_pages():
url1='http://news.sdust.edu.cn/ywcz.htm'
attempt=0
headers={'User-Agent':ua.random} #随机生成UA
res = requests.get(url1,headers=headers)
res.encoding = 'utf-8'
soup = BeautifulSoup(res.text, 'html.parser')
ans = soup.find('div',class_='pb_sys_common pb_sys_normal pb_sys_style1') #找到页码的class
res = ans.find('span',class_='p_t',text=re.compile("[0-9]/[0-9]")).text.split('/')[1]
return int(res)

def get_top_news():
headers={'User-Agent':ua.random} #随机生成UA

res = requests.get(url,headers=headers) #生成headers
# res.raise_for_status() #test res get
res.encoding = 'utf-8'
soup = BeautifulSoup(res.text, 'html.parser')
ans = soup.find_all('tr',id=re.compile("line_u4_*"))#模糊匹配line_u4_*
# print(set(ans))
# ans = set(ans)
for i in ans:
# print("-------------------------")
t = i.find_all('td')
title = t[0]
date = t[1]
url = title.a.attrs['href']
title_str=t[0].text
date_str=t[1].text
url_str = str(url)

# if re.search("http",url_str)==None: #如果没有找到http,自动补全网址
import urllib
url_str=urllib.parse.urljoin(url_auto,url_str)

str1="{title_str} {date_str} {url}".format(title_str=title_str,date_str=date_str,url=url_str)
print(str1)
# print("-------------------------")
# ans = soup.find('tbody')
# print(ans)

def get_news(url_org):
headers={'User-Agent':ua.random} #随机生成UA

if dayi_debug :print("geting url:{}".format(url_org))

attempt = 0
while attempt<=4:
try:
res = requests.get(url_org,headers=headers) #生成headers
res.encoding = 'utf-8' #设置编码格式

if res.status_code!=200: #如果不是200返回值报错。
print([401,"[dayi-err]NOT 200 STATUS,but {},retrying...{}".format(res.status_code,attempt)])
attempt+=1
continue

#开始解析
soup = BeautifulSoup(res.text, 'html.parser')
ans = soup.find_all('tr',id=re.compile("line_u4_*"))#模糊匹配line_u4_*
ls_res=[]
for i in ans:
# print("-------------------------")
t = i.find_all('td')
title = t[0]
date = t[1]
url = title.a.attrs['href']
title_str=t[0].text
date_str=t[1].text
url_str = str(url)

url_str= urllib.parse.urljoin(url_org,url_str) #修复url
str1="{title_str} {date_str} {url}".format(title_str=title_str,date_str=date_str,url=url_str)
# print(str1)
ls_res.append([202,3,title_str,date_str,url_str])
return ls_res
break#退出尝试循环
except Exception as e:
print([501,'[dayi-err]python error:{},unknown'.format(str(e))])
attempt+=1
pass
return None


f=open("debug_test.txt.debug", "w", encoding="utf-8")

def get_other_pages():
res_ls = []
for i in range(pages_cnt):
exect_num = pages_cnt-i-1

url = 'http://news.sdust.edu.cn/ywcz/{}.htm'.format(exect_num)
# url2 = 'https://www.sdust.edu.cn/sylm/kdyw/{}.htm'.format(i)
print("------第{}页_start-------".format(exect_num))
f.write("------第{}页_start-------\n".format(exect_num))

ls = get_news(url)
if ls!=None:
for j in ls:
out_text = "{title_str} {date_str} {url}\n".format(title_str=j[2],date_str=j[3],url=j[4])
f.write(out_text)
publish_time =j[3] #2022-12-15
publish_time_ls = re.findall(r"\d+\.?\d*",publish_time) #解析为[2022,12,15]
publish_time_dt = datetime.datetime(year=int(publish_time_ls[0]),month=int(publish_time_ls[1]),day=int(publish_time_ls[2])) #修改为datetime
j[3]=publish_time_dt #返回值重新修改
res_ls.append(j)

f.write("------第{}页_end-------\n".format(exect_num))
print("------第{}页_end-------".format(exect_num))
return res_ls

# 首页
def get_first_page():
url_first = 'https://news.sdust.edu.cn/ywcz.htm' #第一页

res_ls = [] #返回主要的数据

# print("------第首页_start-------")
f.write("------第首页_start-------\n")

ls = get_news(url_first)
if ls!=None:
for j in ls:
out_text = "{title_str} {date_str} {url}\n".format(title_str=j[2],date_str=j[3],url=j[4])

publish_time =j[3] #2022-12-15
publish_time_ls = re.findall(r"\d+\.?\d*",publish_time) #解析为[2022,12,15]
publish_time_dt = datetime.datetime(year=int(publish_time_ls[0]),month=int(publish_time_ls[1]),day=int(publish_time_ls[2])) #修改为datetime
j[3]=publish_time_dt #返回值重新修改

res_ls.append(j) #保存数据
f.write(out_text)
f.write("------第首页_end-------\n")
# print("------第首页_end-------")
return res_ls

pages_cnt = 10086 #总页数

# get_first_page()
# print(get_other_pages())

# 要闻传真列表的全部获得
def get_all_news():
global pages_cnt #设置全局变量
pages_cnt = get_pages() #获得总页数
ls_ans = []
ls_first = get_first_page()
ls_other = get_other_pages()
ls_all = ls_first+ls_other
print(ls_all)
return ls_all

f = open('ls-all-list.debug', 'w', encoding='utf-8') #调试文件重定向
# ff=open('ls-all.debug','w',encoding='utf-8')
# ff.write(str(get_all_news()))
# ff.close()


if __name__ == '__main__':
get_all_news()
f.close()

# get_all_news()

get_title.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#获得标题,时间,点击量(主要是点击量)
#接口调用: get_title_on_news(url)
#接口返回: [202,3,title.get_text(),publish_time_dt,publish_click_num]
#接口示例: [202, 3, '校党委理论学习中心组开展2022年第十九次集体学习', datetime.datetime(2022, 12, 16, 0, 0), '844']

import requests
import bs4
from bs4 import BeautifulSoup #pip install bs4
from fake_useragent import UserAgent #pip install fake-useragent
#pip install lxml
import urllib
import re
import datetime

def get_click_num(res_num): #获得点击量,如果可以异步就好啦
ua = UserAgent()
url1='http://www.sdust.edu.cn/system/resource/code/news/click/dynclicks.jsp?clickid={click_id}&owner={own}&clicktype=wbnews'.format(click_id=res_num[1],own=res_num[0])
headers={'User-Agent':ua.random}
res = requests.get(url1,headers=headers)
return res.text

def get_title_on_news_main(url:str):
ua = UserAgent()
headers={'User-Agent':ua.random}
res = requests.get(url,headers=headers)
res.encoding = 'utf-8'
soup = BeautifulSoup(res.text, 'html.parser')
title = soup.find('p',class_='title')
publish_time = soup.find('p',class_='time')
publish_click = publish_time.find('script').get_text()
res_num = re.findall(r"\d+\.?\d*",publish_click) #匹配 _showDynClicks("wbnews", 1470981840, 15717) 中的两个数字
publish_click_num = get_click_num(res_num)
publish_time_ls = re.findall(r"\d+\.?\d*",publish_time.get_text())
publish_time_dt = datetime.datetime(year=int(publish_time_ls[0]),month=int(publish_time_ls[1]),day=int(publish_time_ls[2]))

print("标题:{} 时间:{} 点击量:{}".format(title.get_text(),publish_time_dt.strftime("%Y-%m-%d"),publish_click_num))

return [202,3,title.get_text(),publish_time_dt,publish_click_num]

# get_title_on_news(url='http://www.sdust.edu.cn/info/1034/15717.htm')

#错误防止报错
def get_title_on_news(url:str):
att = 0
while att<=4:
try:
return get_title_on_news_main(url)
except Exception as e:
dayi_err_info = [501,'[dayi-error]未知错误,获得页面时失败:{} 尝试重试次数:{}'.format(str(e),att)]
print(dayi_err_info)
att+=1
return [501,'[dayi-error]未知错误,获得页面图片时失败 尝试重试次数:{} url:{}'.format(att,url)]

if __name__=="__main__":
print(get_title_on_news('https://www.sdust.edu.cn/info/1034/15737.htm'))

get_pages.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#获得页面的数据
#感谢:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/
#接口说明: 仅获取内容信息,因为部分内容还没有写,也就是说,目前部分只可以获得纯文字内容
#接口1调用: def get_pages_only_text(url:str): url 为网址内容 (返回的只有文字内容)
#接口1返回: return [202,2,'text',news_only_text] (范围的类型如下)
#接口2调用: def get_pages_pic(url:str):
#接口2返回: [[202, 2, 'pic', 'https://ta.sdust.edu.cn/__local/2/D9/3D/B62E635229FAD159BD3B74F8CA6_EFF684AB_2BB65.jpg', 'https://ta.sdust.edu.cn/info/1025/36630.htm']]
#如果接口返回502,说明出现了问题

# url1 = 'https://ta.sdust.edu.cn/info/1025/36630.htm'
# url2 = 'http://news.sdust.edu.cn/info/1117/77548.htm'

import requests
import bs4
from bs4 import BeautifulSoup #pip install bs4
from fake_useragent import UserAgent #pip install fake-useragent
#pip install lxml
import urllib
# import aiohttp
import asyncio
import os

#
# import sys
# sys.path.append("../../") #for debug
# import lib.dayi_sqlite as dayidb
# db = dayidb.db()

def fix_url(orgin_url,url_need_fix):#URL自动补全
#print(fix_url('http://news.sdust.edu.cn/info/1160/77593.htm','/__local/C/CC/6B/A7534B3E2A44181553A7F6B68B8_E99B0979_18B74.jpg'))
url_str= urllib.parse.urljoin(orgin_url,url_need_fix) #修复url
return url_str

ua = UserAgent()

def get_pages_only_text(url:str):
try:
headers = {'User-Agent': ua.random} # 随机生成UA
res = requests.get(url,timeout=3,headers=headers)
res.encoding = 'utf-8'
soup = BeautifulSoup(res.text, 'html.parser')
news_con = soup.find('div',class_ ='v_news_content')
news_only_text = news_con.get_text().strip()#.replace('\n','')
f=open("out.test.debug","w",encoding="utf-8")
f.write(news_only_text)
f.close()
# print(news_only_text)
return [202,2,'text',news_only_text]
except Exception as e:
return [501,'[dayi-error]获得文字信息出现错误:{} url:{}'.format(str(e),url)]
def get_pages(url:str):
headers={'User-Agent':ua.random} #随机生成UA
res = requests.get(url,headers=headers)#发送请求
res.encoding = 'utf-8' #编码格式
soup = BeautifulSoup(res.text, 'html.parser')
news_con = soup.find('div',class_ ='v_news_content')
# news_title = soup.find('div',class_='d-newsxq-tit').text
# print("标题:[{}]".format(news_title))
ls = []
for i in news_con: #图片添加到目录
if i == '\n': #不优雅的去除多余的内容
continue
if i.find("img") !=None: #寻找图片
img1 = i.find('img')
img_=img1.attrs['src']
pic_url = fix_url(url,img_)
#db.insert_pic_db(pic_url)
print(pic_url)


print(i.get_text())
# print(i)

def get_pages_pic_main(url:str):
ls_res = []
headers={'User-Agent':ua.random} #随机生成UA
res = requests.get(url,headers=headers)#发送请求
res.encoding = 'utf-8' #编码格式
soup = BeautifulSoup(res.text, 'html.parser')
news_con = soup.find('div',class_ ='v_news_content')

for i in news_con: #图片添加到目录
if i == '\n': #不优雅的去除多余的
continue
if i.find("img") !=None: #寻找图片
img1 = i.find('img')
img_=img1.attrs['src']
pic_url = fix_url(url,img_) #图片的链接
from_url = url
#db.insert_pic_db(pic_url)
ls_tmp = [202,2,'pic',pic_url,from_url]
#print(pic_url)
ls_res.append(ls_tmp)
return ls_res

def get_pages_pic(url:str):
att = 0
while att<=4:
try:
return get_pages_pic_main(url)
except Exception as e:
dayi_err_info = [501,'[dayi-error]未知错误,获得页面图片时失败:{} 尝试重试次数:{}'.format(str(e),att)]
print(dayi_err_info)
att+=1
return [501,'[dayi-error]未知错误,获得页面图片时失败:{} 尝试重试次数:{}'.format(str(e),att)]
if __name__ == '__main__':
print(get_pages_only_text("http://news.sdust.edu.cn/info/1006/18350.htm"))
print(get_pages_pic('http://news.sdust.edu.cn/info/1006/18350.htm'))
# print(get_pages_pic(url=url1))

# async def async_get_pages_only_text_main(url:str):
# async with aiohttp.ClientSession() as session:
# headers={'User-Agent':ua.random}
# async with session.get(url,headers=headers) as res:
# res.encoding = 'utf-8'
# soup = await BeautifulSoup(res.text, 'html.parser')
# news_con = soup.find('div',class_ ='v_news_content')
# news_only_text = news_con.get_text().strip()#.replace('\n','')
# return [202,2,'text',news_only_text]

# async def async_get_pages_pic(url:str):
# # return await async_get_pages_only_text_main(url)
# att = 0
# while att<=4:
# try:
# return await async_get_pages_only_text_main(url)
# except Exception as e:
# dayi_err_info = [501,'[dayi-error]未知错误,获得页面图片时失败:{} 尝试重试次数:{}'.format(str(e),att)]
# print(dayi_err_info)
# att+=1
# return [501,'[dayi-error]未知错误,获得页面图片时失败:{} 尝试重试次数:{}'.format(str(e),att)]
# import time

# time_start=time.time()
# asyncio.run(async_get_pages_pic('http://www.sdust.edu.cn/info/1034/15734.htm'))

爬虫库里的sqlite class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
import sqlite3
import datetime,time
import uuid #用于插入媒体的时候判断唯一的信息

import os #解析文件名(手动解析也是可以的,但是 str(media_url).split('.')[-1].lower() 感觉肯定会有问题)
from urllib.parse import urlparse #用这个解析url比较安全

# 多线程版sqlite
from sqlalchemy import create_engine

__dayi_debug__ = True
__dayi_rm_db__ = False

class dayi_db_ovo:
def __init__(self,dbpath='./dayi-db.db'):
self.dbpath = dbpath
self.__del_data_base__() #删除数据库,调试用。
self.__enter__(dbpath=dbpath)

def __enter__(self,dbpath='./dayi-db.db'):
if __dayi_debug__:print([201,'[dayi-info]数据库目录:'+dbpath])

try:
self.init_con(dbpath=dbpath)
if __dayi_debug__:print([201,'[dayi-info]初始化连接成功'])
except Exception as e:
if __dayi_debug__:print([401,'[dayi-error]初始化连接失败:'+str(e)])

try:
self.build_tables()
if __dayi_debug__:print([201,'[dayi-info]数据表创建成功'])
except Exception as e:
if __dayi_debug__:print([401,'[dayi-error]数据表创建失败:'+str(e)])

return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("[ovo?]")
self.conn.close()

def init_con(self,dbpath):
path = dbpath
self.conn = create_engine('sqlite:///{}?check_same_thread=False'.format(dbpath),echo=False)
self.cur = self.conn
return self.conn

def __del_data_base__(self):

if __dayi_rm_db__ == False: #不删除数据库
if __dayi_debug__:
print([101,'[dayi-info]不进行删除数据库'])
return [101,'[dayi-info]不进行删除数据库']

dbpath = self.dbpath #获得数据库目录

if __dayi_debug__: print([203,'[dayi-warning]Will delete database:'+dbpath])
try:
import os
os.remove(self.dbpath)
if __dayi_debug__: print([203,'[dayi-warning]Deleted database:'+dbpath])
except Exception as e:
if __dayi_debug__:print([401,'[dayi-error]数据库删除失败:'+str(e)])

def sql_command(self,sql_text):
self.cur.execute(sql_text)
ans = self.conn.commit()
return ans

def build_tables(self):
table_create_content_list="""
CREATE TABLE IF NOT EXISTS content_list(
"id" INTEGER NOT NULL,
"content_title" text,
"content_publish_time" TEXT,
"content_publish_time_unix" TEXT,
"content_clicks_num" TEXT,
"content_only_text" TEXT,
"content_only_pic" TEXT,
"content_url" TEXT,
"content_all" TEXT,
"json" TEXT,
PRIMARY KEY ("id")
);
"""

table_create_media_list="""
CREATE TABLE IF NOT EXISTS media_list (
"id" INTEGER NOT NULL,
"media_date_str" TEXT,
"media_uuid" TEXT,
"media_url" TEXT,
"media_local_path" TEXT,
"media_type" TEXT,
"media_file_size" TEXT,
"media_is_downloaded" TEXT,
"json" TEXT,
PRIMARY KEY ("id")
);
"""

table_covid_data_list="""
CREATE TABLE IF NOT EXISTS covid_19_data (
"id" INTEGER NOT NULL,
"date" TEXT,
"date_unix" interger,
"province_code" TEXT,
"province_name" TEXT,
"seem_add" TEXT,
"seem_all" TEXT,
"sure_add" TEXT,
"sure_all" TEXT,
"die_add" TEXT,
"die_all" TEXT,
"json" TEXT,
PRIMARY KEY ("id")
);
"""

self.cur.execute(table_create_content_list)
self.cur.execute(table_create_media_list)
self.cur.execute(table_covid_data_list)

# self.conn.commit()#保存数据库
return

def insert_covid_date(self,date:datetime,province_name,province_code,sure_add,sure_all,die_add,die_all):
table_name = 'covid_19_data' #表名
date_unix = time.mktime(date.timetuple()) #unix time
date_str = date.strftime("%Y%m%d") #20220101

# print(date_unix)
sql_command="insert or ignore into {table_name} (date,date_unix,province_name,province_code,sure_add,sure_all,die_add,die_all) values ('{date_str}','{date_unix}','{province_name}','{province_code}','{sure_add}','{sure_all}','{die_add}','{die_all}');".format(table_name=table_name,date_str=date_str,date_unix=date_unix,province_code=province_code,province_name=province_name,sure_add=sure_add,sure_all=sure_all,die_add=die_add,die_all=die_all)

# print(sql_command)

self.cur.execute(sql_command)
# self.conn.commit()

def insert_pic_db(self, media_url, media_local_path='./data/news.sdust/data/pic/', media_type='jpg', date: datetime=datetime.datetime(2022,1,1)):
"""图片插入
"media_url" TEXT, 图片URL
"media_local_path" TEXT 图片的本地存储路径,仅目录,图片名字用UUID生成
"media_type" TEXT, 图片的类型
"media_file_size" TEXT, 图片的大小
"media_uuid" TEXT, 图片UUID
"media_is_downloaded" TEXT, 图片是否已经下载
"media_date_str" TEXT, 图片的日期
Args:
参数其实只有url有用
"""

table_name='media_list' #表名
media_uuid=uuid.uuid1() #生成UUID
date_str = date.strftime("%Y%m%d")
date_str_month = date.strftime("%m")
date_str_year = date.strftime("%Y")

_,media_type=os.path.splitext(urlparse(media_url).path) #这样相对安全一点
# media_type=str(media_url).split('.')[-1].lower() #这样写其实不是很好 #获得扩展名

media_local_path_with_file_name = media_local_path+'{}/{}/'.format(date_str_year,date_str_month)+str(media_uuid)+media_type #获得本地路径
# print(media_local_path_with_file_name)
sql_command= "insert or ignore into {table_name} (media_uuid,media_url,media_local_path,media_type,media_is_downloaded,media_date_str) values ('{media_uuid}','{media_url}','{media_local_path}','{media_type}','{media_is_downloaded}','{media_date_str}')".format(table_name=table_name,media_uuid=media_uuid,media_local_path=media_local_path_with_file_name,media_type=media_type.split('.')[-1],media_url=media_url,media_is_downloaded=0,media_date_str=date_str)
# print(sql_command)
self.cur.execute(sql_command)
# self.conn.commit() #IO太慢啦
return media_uuid
def insert_content_db(self,content_title,date:datetime,content_clicks_num,content_only_text,content_url,content_only_pic='',content_all=''):
"""插入图片的信息到数据库中,请注意,此方法需要手动进行更新数据库
CREATE TABLE IF NOT EXISTS content_list(
"id" INTEGER NOT NULL,
"content_title" text,
"content_publish_time" TEXT,
"content_publish_time_unix" TEXT,
"content_clicks_num" TEXT,
"content_only_text" TEXT,
"content_only_pic" TEXT,
"content_url" TEXT,
"content_all" TEXT,
"json" TEXT,
PRIMARY KEY ("id")
);
"""
table_name='content_list' #表名
date_unix = time.mktime(date.timetuple()) #unix time
date_str = date.strftime("%Y%m%d") #20220101

sql_command= "insert or ignore into {table_name} (content_title,content_publish_time,content_publish_time_unix,content_clicks_num,content_only_text,content_only_pic,content_url,content_all) values ('{content_title}','{content_publish_time}','{content_publish_time_unix}','{content_clicks_num}','{content_only_text}','{content_only_pic}','{content_url}','{content_all}')".format(content_title=content_title,content_publish_time=date_str,content_publish_time_unix=date_unix,content_clicks_num=content_clicks_num,content_only_text=content_only_text,content_only_pic=content_only_pic,content_url=content_url,content_all=content_all,table_name=table_name)
# print(sql_command)
self.cur.execute(sql_command)
return

def commit_db(self): #保存数据库,因为IO太慢了,得分开
# self.conn.commit()
# self.conn.commit()
return





# db.insert_pic_db("http://iuqiweuoqwe.qeryuiqewirqe.cc/rq134132.png12.21.33")

# print(db.sql_command(sql_text="Select * from covid_19_data"))


# db.insert_covid_date(dt,"山东省","37000000",sure_add=1,sure_all=10,die_all=0,die_add=0)

# dt = datetime.datetime(2022,1,1)
# db = dayi_db_ovo()
# db.insert_covid_date(dt,"山东省","37000000",sure_add=1,sure_all=10,die_all=0,die_add=0)
# db.insert_content_db("标题",dt,"2333","猴子开飞机",'http://monkey',"http://pic_monkey")
# db.commit_db()

一个平平无奇的flask的模板,就是那个可以实时生成词云的那个 sdust_content_easy.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SQLite 数据库</title>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/jquery-3.6.0.min.js"></script>
</head>
<body>
<div class="container">
<!-- Header -->
<h1>内容列表</h1>

<!-- 对话框 -->
<div class="modal fade" id="wordcloudModal" tabindex="-1" role="dialog" aria-labelledby="wordcloudModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="wordcloudModalLabel">词云生成中,请稍候...</h5>
</div>
<div class="modal-body">
<div class="progress">
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div>
</div>
<div id="countdown"></div>
<img id="wordCloudImage" src="" alt="词云图片" style="width: 100%; display: none;">
</div>
</div>
</div>
</div>

<!-- Table -->
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>标题</th>
<th>发布时间</th>
<th>发布时间 (Unix)</th>
<th>点击量</th>
<th>仅文字</th>
<th>URL</th>
<!-- <th>全部</th>-->
<th>JSON</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for content in content_list %}
<tr>
<!-- Table Data -->
<td>{{ content[0] }}</td>
<td>{{ content[1] }}</td>
<td>{{ content[2] }}</td>
<td>{{ content[3] }}</td>
<td>{{ content[4] }}</td>
<td>{{ content[5][:50] }}...</td>
{% if content[6] %}
<td><a href="{{content[6]}}" target="_blank">原文链接:{{content[6][7:10]+content[6][-10:]}}</a></td>
{% else %}
<td>链接为空</td>
{% endif %}


<td>{{ content[7] }}</td>
<td>{{ content[8] }}</td>
<!-- <td>{{ content[9] }}</td>-->

<td>
<button class="btn btn-primary generate-wordcloud" data-id="{{ content[0] }}">生成词云!</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>

<!-- Pagination -->
<nav>
<ul class="pagination">
{% if current_page > 1 %}
<li class="page-item">
<a class="page-link" href="{{ url_for('indexovoovov_', page=current_page-1) }}">&laquo;</a>
</li>
{% endif %}

{% for page in range(1, total_pages + 1) %}
<li class="page-item {% if page == current_page %}active{% endif %}">
<a class="page-link" href="{{ url_for('indexovoovov_', page=page) }}">{{ page }}</a>
</li>
{% endfor %}

{% if current_page < total_pages %}
<li class="page-item">
<a class="page-link" href="{{ url_for('indexovoovov', page=current_page+1) }}">&raquo;</a>
</li>
{% endif %}
</ul>
</nav>
</div>

<!-- Bootstrap JS -->
<script src="https://cdn.staticfile.net/jquery/3.7.1/jquery.slim.min.js"></script>
<script src="https://cdn.staticfile.net/popper.js/2.5.4/cjs/popper.min.js"></script>
<script src="https://cdn.staticfile.net/bootstrap/4.5.3/js/bootstrap.min.js" ></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js"></script>-->
<!-- <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>-->

<script>

$(document).ready(function() {
$('.generate-wordcloud').click(function() {
var contentId = $(this).data('id');

imgSrc='https://cmd.dayi.ink/uploads/upload_658b855d1cf0fde77227294bfe3e2936.png'
$('#wordCloudImage').attr('src', imgSrc).show();



// 显示对话框
$('#wordcloudModal').modal('show');

// 启动进度条
var progress = 0;
var progressBar = $('.progress-bar');
var progressInterval = setInterval(function() {
progress += 1;
progressBar.css('width', progress + '%').attr('aria-valuenow', progress);
if (progress >= 100) {
clearInterval(progressInterval);
}
}, 200); // 假设总时间为20秒,每200毫秒更新一次进度条

// 启动倒计时
var countdown = 20;
var count_modified = 0;
var countdownElement = $('#countdown');
var countdownInterval = setInterval(function() {
if(count_modified){ //倒计时被修改
clearInterval(countdownInterval);
return
}
countdownElement.text('倒计时:' + countdown + ' 秒');
countdown--;
if (countdown < 0) {
clearInterval(countdownInterval);
countdownElement.text('词云生成完成!');
$("#wordcloudModalLabel").text("倒计时结束了,但是图呢?")
}
}, 1000);

// 使用XHR发送请求生成词云
var xhr = new XMLHttpRequest();
xhr.open('GET', '/generate_wordcloud/' + contentId, true);
xhr.onload = function() {
if (xhr.status === 200) {
count_modified = 1
// 词云生成成功,在页面中显示词云图片
console.log(xhr.responseText);
countdownElement.text('词云生成完成!');
$("#wordcloudModalLabel").text("词云生成完成!")
$("#wordcloudModalLabel").text("词云生成已完成,查看下方图片!");
progress = 100
countdown = -1
// 可以在此处添加相应的代码

var imgSrc = JSON.parse(xhr.responseText).url; // Assuming the response contains the image URL
console.log("dayi_debug:"+imgSrc)
$('#wordCloudImage').attr('src', imgSrc).show();
} else {
// 处理错误
count_modified = 1
console.error(xhr.statusText);
progress = 100
countdown = -1
countdownElement.text('词云生成错误!'+xhr.statusText);
$("#wordcloudModalLabel").text("词云生成错误!");
}
};
xhr.onerror = function() {
console.error('请求错误');
};
xhr.oncomplete = function() {
// 请求完成后,关闭对话框
$('#wordcloudModal').modal('hide');
};
xhr.send();
});
});

</script>
</body>
</html>