Python入门项目:一个简单的办公自动化需求

news2026/2/14 12:32:47

文章目录

    • 引言
    • 项目介绍
    • 爬取数据:BeautifulSoup
    • 写入csv:csv标准库或`pandas`
    • 写入xlsx
      • 用`pandas`写入
      • 用`styleframe`完成样式美化
    • 参考资料

引言

前段时间,我npy说有一个很烦人的需求:有一个文章列表页面,总共10页,每页有30篇文章的标题、链接和日期。她领导希望把这些数据汇总进一个excel表格。她们公司有后台,由技术部的人负责维护,但技术部的后端可能是因为不想加班,并不想帮她干这个活。她在某个周二接的需求,跟领导说这事很麻烦,而且还有其他并行需求,需要到周五才能交付。我听后,笑道:“在你们公司摸鱼也太容易了。”

样本地址。值得注意的是,页面上显示只有10页数据,但实际上这10页数据仅包含了2024年的大部分文章,还有一小部分文章在第10页之后。经测试,翻到第21页时才会返回“页面地址已失效”。

面对办公自动化问题,需要考虑什么工具是你用得最趁手的。如果上述需求的数据量很小,比如10条,那就不必写代码了,可以直接手动解决,或者粘贴给LLM,让LLM处理。如果可以接受JS的数据结构格式,那么可以直接在控制台写一小段JS来解决,也不需要开IDE写Python代码。

获取某一页的文章数据:

const res = [...document.getElementById('pagelist').querySelectorAll('a')].map((a) => {
    const title = a.querySelector('.one-line')?.innerText
    const link = a.href
    return [title, link]
});

// 使用方式:开两个页面,一个页面在当前页,另一个页面在下一页。当前页输出了到当前页为止的所有文章组成的数组,记为arr。进入另一个页面,执行上述代码,然后把arr粘贴到这个页面,然后输入“.push(...res)”,按回车,拿到下一页的文章数组。重复若干次

获取所有数据后,找出疑似重复的文章:

let dat = 300 lines data;
let tmp = new Set();
let duplicate = [];
dat.reduce((res, v) => {
    if (tmp.has(v[0])) {
        duplicate.push(v);
        return res;
    }
    tmp.add(v[0]);
    res.push(v);
    return res;
}, [])

好了,接下来聊聊怎么用Python做这个需求。

作者:hans774882968以及hans774882968以及hans774882968

本文52pojie:https://www.52pojie.cn/thread-1994769-1-1.html

本文juejin:https://juejin.cn/post/7452610281720184832

本文CSDN:https://blog.csdn.net/hans774882968/article/details/144750109

环境:

  • Python 3.7.6
  • pytest 7.4.4
  • pandas 1.3.5, xlwt 1.3.0, styleframe 4.2

项目介绍

项目传送门。这个项目是麻雀虽小五脏俱全,展示了爬取前的数据准备、爬取后如何存储数据、如何进行数据处理等方面的技巧。项目结构:

GX-LOTTERY-CRAWLER
│  .gitignore
│  article.py to_different_outputs.py也用到Article类,所以拆分出来
│  lottery_gx_mid.txt 内容和lottery_gx_out.txt一样。样本页面数据大约每天多一条,没必要每次运行都爬取,因此产生了这个文件作为桥梁。将数据写入数据库有点小题大做,用txt存即可
│  lottery_gx_out.csv 爬到的数据输出为csv。这个csv是用csv标准库生成的
│  lottery_gx_out.p.csv 爬到的数据输出为csv。这个csv是用pandas生成的,所以加了个.p作为区分
│  lottery_gx_out.txt 爬到的数据输出为txt
│  lottery_gx_out.xls 爬到的数据输出为xls
│  lottery_gx_out.xlsx 爬到的数据输出为xlsx
│  main.py 一开始只有这个文件,后面拆分出了to_different_outputs.py
│  test_main.py 单测
│  to_different_outputs.py 爬到的数据输出为多种格式
│
├─coverage 单测输出
  │  report.html
  │
  └─assets
          style.css

单测命令:pytest --html=coverage/report.html

我们将是否发HTTP请求获取数据,以及输出形式的决定权交给用户。所以要用到argparse标准库,documentation。相关代码:

import argparse

def str2bool(v):
    if isinstance(v, bool):
        return v
    if v.lower() in ('yes', 'true', 't', 'y', '1'):
        return True
    elif v.lower() in ('no', 'false', 'f', 'n', '0'):
        return False
    raise argparse.ArgumentTypeError('Bool expected')


def main():
    arg_parser = argparse.ArgumentParser(description='lottery gx articles fetch')
    arg_parser.add_argument(
        '-out', '-o', default='txt', choices=('txt', 'csv', 'xls', 'xlsx', 'p.csv'),
        help='output file format. p.csv means using pandas to write to csv file'
    )
    arg_parser.add_argument(
        '-local', '-l', type=str2bool, default=True, help='load data from local'
    )
    cmd_args = arg_parser.parse_args()
    out_opt = cmd_args.out
    load_from_local = cmd_args.local

爬取数据:BeautifulSoup

样本网站很粗糙,需要的数据在HTML里就能拿到。之前已经知道,总共有20页有效数据,不妨将这个总页码在代码里写死。

proxies = {
    'http': None,
    'https': None
}

def fetch_data_from_website():
    all_articles: List[Article] = []
    for i in range(1, 21):
        if i > 1:
            rest_time = random.uniform(0.5, 1)
            print('Have a rest for %.2fs' % (rest_time))
            time.sleep(rest_time)
        url = f'http://www.lottery.gx.cn/sylm_171188/jdzx/index_{i}.html'
        response = requests.get(url, headers=headers, proxies=proxies)
        print('dbg', i, response.text[:50])
        articles = get_articles_from_html(response.text)
        all_articles.extend(articles)
    return all_articles

数据格式示例:

<ul class="news-list" id="pagelist">
    <li>
        <a href="http://www.lottery.gx.cn/sylm_171188/jdzx/376174.html">
            <span class="one-line">
                奥运冠军盛李豪、黄雨婷、谢瑜走进“广西体彩大讲堂”分享夺金背后的故事
            </span>
            <span>
                (
                2024-12-12)
            </span>
        </a>
    </li>
    <li>
        <a href="http://www.lottery.gx.cn/sylm_171188/jdzx/376035.html">
            <span class="one-line">
                在逆境中绽放,体彩点亮我的人生希望——一名特殊体彩人的自述
            </span>
            <span>
                (
                2024-12-10)
            </span>
        </a>
    </li>
</ul>

用bs4解析即可。直接看代码吧:

def get_articles_from_html(html_txt: str) -> List[Article]:
    bs = BeautifulSoup(html_txt, 'html.parser')
    ul = bs.find(id='pagelist')
    a_links = ul.find_all('a')
    res = []
    for a_link in a_links:
        link = a_link['href']
        span_title = a_link.find(class_='one-line')
        title = span_title.text.strip()
        span_date = a_link.find_all('span')[-1] # 取a标签下的最后一个span
        date = span_date.text.strip('() \n\t')
        article = Article(date, link, title)
        res.append(article)
    return res

写入csv:csv标准库或pandas

用csv标准库写入相对麻烦些:

def articles2csv(articles: List[Article]):
    with open('lottery_gx_out.csv', 'w', encoding='utf-8', newline='') as f:
        csv_writer = csv.writer(f)
        csv_writer.writerow(('date', 'link', 'title'))
        csv_rows = articles_to_2d_array(articles)
        csv_writer.writerows(csv_rows)

pandas写入则相当简单:

import pandas as pd

def articles2csv_pandas(articles: List[Article]):
    df = pd.DataFrame(articles)
    df.to_csv('lottery_gx_out.p.csv', index=False)

安装pandas很简单,直接pip install pandas。大约占10MB磁盘空间。

根据pandas DataFrame documentation,pd.DataFrame可以接受dataclass数组,所以我们将Article改造为dataclass即可直接传入。

from dataclasses import dataclass


@dataclass
class Article:
    date: str
    link: str
    title: str

    def __str__(self) -> str:
        return f'{self.date} {self.link} {self.title}'

    def __eq__(self, value: object) -> bool:
        return self.date == value.date and self.link == value.link and self.title == value.title

pandas默认输出的csv的编码为utf-8。如果想用Excel查看,建议点击Excel的“数据”tab→“自文本”按钮来加载数据。

写入xlsx

pandas写入

我们写下这段代码,运行看看效果:

def get_duplicate_and_unique(articles: List[Article]):
    st = set()
    duplicate: List[Article] = []
    unq: List[Article] = []
    for article in articles:
        title = article.title
        if title in st:
            duplicate.append(article)
            continue
        st.add(title)
        unq.append(article)
    return duplicate, unq

def articles2xlsx(articles: List[Article]):
    df_all = pd.DataFrame(articles)
    duplicate, unq = get_duplicate_and_unique(articles)
    df_unq = pd.DataFrame(unq)
    df_duplicate = pd.DataFrame(duplicate)
    df_arr = (df_all, df_unq, df_duplicate)
    with pd.ExcelWriter('lottery_gx_out.xlsx') as writer:
        for df, sheet_name in zip(df_arr, XLS_SHEET_NAMES):
            df.to_excel(writer, sheet_name=sheet_name, index=False)

数据是成功写入了,但默认的列宽都太小了,需要想办法调大些。

styleframe完成样式美化

问了下doubao AI,在用pandas写入Excel的场景下,调整列宽是比较麻烦的。之前可用的set_column方法已经废弃了('Worksheet' object has no attribute 'set_column'),而现在需要直接导入openpyxl

import pandas as pd
from openpyxl.utils import get_column_letter
from openpyxl.styles import Alignment
from openpyxl import load_workbook

# 创建一个示例DataFrame
data = {'姓名': ['张三', '李四', '王五'],
        '年龄': [20, 25, 30],
        '成绩': [80, 90, 95]}
df = pd.DataFrame(data)

# 将DataFrame写入Excel文件
writer = pd.ExcelWriter('example.xlsx', engine='openpyxl')
df.to_excel(writer, sheet_name='Sheet1', index=False)
workbook = writer.book
worksheet = writer.sheets['Sheet1']

# 设置列宽
for column in df:
    column_width = max(df[column].astype(str).map(len).max(), len(column))
    col_letter = get_column_letter(df.columns.get_loc(column) + 1)
    worksheet.column_dimensions[col_letter].width = column_width * 1.2
writer.save()

因此打算用styleframe调整列宽。既然用styleframe可以方便地完成样式美化的工作,那么不妨多做点事情。安装styleframe很简单,只需要pip install styleframe

问了AI(Prompt:“有一个学生列表和一个老师列表。希望生成一个excel文件,将它们分别写入不同的sheet。需要使用Python的pandas和styleframe,且要使用styleframe调整列宽、设置表头样式”),注意到styleframe.StyleFrame也有一个to_excel()方法,在此和pandasto_excel一样地调用即可。

from styleframe import StyleFrame

def articles2xlsx(articles: List[Article]):
    # ...
    with pd.ExcelWriter('lottery_gx_out.xlsx') as writer:
        for df, sheet_name in zip(df_arr, XLS_SHEET_NAMES):
            sf = StyleFrame(df)
            sf.set_column_width_dict(
                {
                    'date': 15,
                    'link': 20,
                    'title': 40,
                }
            )
            sf.to_excel(writer, sheet_name=sheet_name, index=False)

再次打开Excel文档可以看到,虽然列宽仍然不够大,但每个单元格的内容都是完整显示的,每一行的文本都居中。这是因为源码里:

# StyleFrame 构造函数有:
self._default_style = styler_obj or Styler()
# 而 Styler 构造函数有:
horizontal_alignment: str = utils.horizontal_alignments.center,
vertical_alignment: str = utils.vertical_alignments.center, # 顺便说下,默认垂直也居中
shrink_to_fit: bool = True

接下来我们参考参考链接2,给表格多加点样式。

from styleframe import StyleFrame, Styler, utils

def articles2xlsx(articles: List[Article]):
    df_all = pd.DataFrame(articles)
    duplicate, unq = get_duplicate_and_unique(articles)
    df_unq = pd.DataFrame(unq)
    df_duplicate = pd.DataFrame(duplicate)
    df_arr = (df_all, df_unq, df_duplicate)
    with pd.ExcelWriter('lottery_gx_out.xlsx') as writer:
        for df, sheet_name in zip(df_arr, XLS_SHEET_NAMES):
            sf = StyleFrame(df)
            header_style = Styler(
                font_color='#2980b9',
                bold=True,
                font_size=14,
                horizontal_alignment=utils.horizontal_alignments.center,
                vertical_alignment=utils.vertical_alignments.center,
            )
            content_style = Styler(
                font_size=12,
                horizontal_alignment=utils.horizontal_alignments.left,
            )
            sf.apply_headers_style(header_style)
            sf.apply_column_style(sf.columns, content_style)

            # it's a pity that we have to set font_size and horizontal_alignment again
            row_bg_style = Styler(
                bg_color='#bdc3c7',
                font_size=12,
                horizontal_alignment=utils.horizontal_alignments.left,
            )
            indexes = list(range(1, len(sf), 2))
            sf.apply_style_by_indexes(indexes, styler_obj=row_bg_style)

            sf.set_column_width_dict(
                {
                    'date': 15,
                    'link': 20,
                    'title': 40,
                }
            )
            sf.to_excel(writer, sheet_name=sheet_name, index=False)

酷!

参考资料

  1. https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html
  2. https://www.cnblogs.com/wang_yb/p/18070891

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2266432.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入浅出 MyBatis | CRUD 操作、配置解析

3、CRUD 3.1 namespace namespace 中的包名要和 Dao/Mapper 接口的包名一致&#xff01; 比如将 UserDao 改名为 UserMapper 运行发现抱错&#xff0c;这是因为 UserMapper.xml 中没有同步更改 namespace 成功运行 给出 UserMapper 中的所有接口&#xff0c;接下来一一对…

基于c语言的union、字符串、格式化输入输出

结构体之共用体union 共用体也叫联合体&#xff0c;其关键词为union 与结构体不同的是&#xff0c;共用体所开辟的存储空间仅仅为其中最长类型变量的存储空间而不是全部变量的存储空间&#xff0c;由于同一内存单元在同一时间内只能存放其中一种的数据类型&#xff0c;因此在每…

IntelliJ Idea常用快捷键详解

文章目录 IntelliJ Idea常用快捷键详解一、引言二、文本编辑与导航1、文本编辑2、代码折叠与展开 三、运行和调试四、代码编辑1、代码补全 五、重构与优化1、重构 六、使用示例代码注释示例代码补全示例 七、总结 IntelliJ Idea常用快捷键详解 一、引言 在Java开发中&#xff…

AI开发:使用支持向量机(SVM)进行文本情感分析训练 - Python

支持向量机是AI开发中最常见的一种算法。之前我们已经一起初步了解了它的概念和应用&#xff0c;今天我们用它来进行一次文本情感分析训练。 一、概念温习 支持向量机&#xff08;SVM&#xff09;是一种监督学习算法&#xff0c;广泛用于分类和回归问题。 它的核心思想是通过…

Linux-----进程处理(子进程创建)

【尚硅谷嵌入式Linux应用层开发&#xff0c;linux网络编程&#xff0c;linux进程线程&#xff0c;linux文件io】https://www.bilibili.com/video/BV1DJ4m1M77z?p35&vd_source342079de7c07f82982956aad8662b467 main函数 fork创建进程 fork()参数 /*** brief 创建一个子进…

一种寻路的应用

应用背景 利用长途车进行货物转运的寻路计算。例如从深圳到大连。可以走有很多条长途车的路线。需要根据需求计算出最合适路线。不同的路线的总里程数、总价、需要的时间不一样。客户根据需求进行选择。主要有一些细节&#xff1a; 全国的长途车车站的数据的更新&#xff1a; …

2. FPGA基础了解--全局网络

前言 引入扇出的概念介绍FPGA中的全局网络为后续时序优化埋下伏笔 扇出 在FPGA设计中扇出是一个重要的概念&#xff0c;所谓的扇出就是一个控制信号所能控制的数据信号的总个数&#xff0c;比如ctrl信号的扇出就是16 reg ctrl 0; reg [15:0] out 0; always (posedge c…

Excel中一次查询返回多列

使用Excel或wps的时候&#xff0c;有时候需要一次查询返回多列内容&#xff0c;这种情况可以选择多次vlookup或者多次xlookup&#xff0c;但是这种做法费时费力不说&#xff0c;效率还有些低下&#xff0c;特别是要查询的列数过多时。我放了3种查询方法&#xff0c;效果图&…

智能化军事【五】精确制导武器智能化实现

文章目录 前言精确制导武器智能化实现基于深度学习实现的智能化功能基于强化学习实现的智能化功能强化学习深度强化学习 网络模型轻量化网络剪枝&#xff08;通道剪枝&#xff09;技术层剪枝权值量化技术低秩近似技术知识蒸馏技术强化学习联合训练 解决有效训练样本不足或获取困…

解锁高效密码:适当休息,让学习状态满格

一、“肝帝” 的困境 在当今竞争激烈的职场中&#xff0c;“肝帝” 现象屡见不鲜。超长工时仿佛成为了许多行业的 “标配”&#xff0c;从互联网企业的 “996”“007”&#xff0c;到传统制造业的轮班倒、无休无止的加班&#xff0c;员工们的工作时间被不断拉长。清晨&#xff…

asp.net 高校学生勤工俭学系统设计与实现

博主介绍&#xff1a;专注于Java&#xff08;springboot ssm 等开发框架&#xff09; vue .net php python(flask Django) 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找…

WebRTC服务质量(12)- Pacer机制(04) 向Pacer中插入数据

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

RTMW:实时多人2D和3D 全人体姿态估计

单位&#xff1a;上海AI实验室 代码&#xff1a;mmpose/tree/main/projects/rtmpose 系列文章目录 RTMO: 面向高性能单阶段的实时多人姿态估计 目录 系列文章目录摘要一、背景二、相关工作2.1 自上而下的方法。2.2 坐标分类。2.3 3D Pose 3 实验方法3.1.1 任务限制3.1.3训练技…

AI智能养站神器-SEO助理原创文章批量生成发布工具

很多站长最头疼的就是网站每天的内容更新&#xff0c;因为不知道写什么&#xff0c;采集被人的文章又会被定义为抄袭&#xff0c;而且现在伪原创已经没有多大的效果了&#xff0c;所以今天给大家分享的就是一款AI智能养战神器-SEO助理原创文章批量生成发布工具。 这款工具支持…

用Python开启人工智能之旅(三)常用的机器学习算法与实现

第三部分&#xff1a;常用的机器学习算法与实现 用Python开启人工智能之旅&#xff08;一&#xff09;Python简介与安装 用Python开启人工智能之旅&#xff08;二&#xff09;Python基础 用Python开启人工智能之旅&#xff08;三&#xff09;常用的机器学习算法与实现 用Pyt…

FD(File Descriptor)泄漏

File Descriptor是Linux下概念&#xff0c;fd 是 int类型非负数&#xff01; 进程打开File&#xff0c;Socket&#xff0c;Pipe后生成一个File Descriptor&#xff0c;它是打开这个系统资源的标识符。 Linux每个进程fd最大1024个&#xff0c;超过之后进程 crash&#xff0c;c…

英语单词拼读小程序开发制作介绍

英语单词拼读小程序开发制作介绍本英语单词拼读小程序系统开发的主要功能有&#xff1a; 1、按年级分类展示每个年级阶段的英语单词信息。 2、点击选择的单词进入单词拼读页面&#xff0c;展示英语单词的拼读音标、中文意思、单词发音、拆分词汇发音、用户通过朗读发音对比。通…

TCP客户端模拟链接websocket服务端发送消息(二)

兄弟们&#xff0c;我来填坑了&#xff0c;o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o&#xff0c;前几天写了个tcp模拟websocket客户端的以为完成&#xff0c;后面需要发送消息给服务端&#xff0c;以为简单不就是一个发送消息么&#xff0c;这不是一…

Docker 镜像加速访问方案

在数字化时代&#xff0c;Docker以其轻量级和便捷性成为开发者和运维人员的首选容器技术。然而自2023年5月中旬起&#xff0c;Docker Hub 的访问速度较慢或不稳定&#xff0c;这对依赖Docker Hub拉取镜像的用户来说无疑是一个挑战。本文将提供 Docker Hub 访问的一系列替代方案…

牛客网刷题 ——C语言初阶——BC112小乐乐求和

1.牛客网刷题 ——C语言初阶 牛客网&#xff1a;BC112小乐乐求和 小乐乐最近接触了求和符号Σ&#xff0c;他想计算的结果。但是小乐乐很笨&#xff0c;请你帮助他解答。 输入描述: 输入一个正整数n (1 ≤ n ≤ 109) 输出描述: 输出一个值&#xff0c;为求和结果。 示例1 输…