使用Python脚本订阅arxiv每日上新论文
本文涉及代码主要由deepseek提供。
这个事情主要是为了能够每天订阅到新的论文,另外是能够自动整理到知识库里面去,免得像邮件订阅或者 CSS 订阅一样去搬运。
主要涉及以下内容:
- 根据关键字搜索 arxiv 的论文,并提取信息
- 将摘要内容翻译成中文
- 按照指定格式,保存成 md 文件到指定位置
爬虫
搜索 arxiv 信息直接使用了 [[Python]] 的库 arxiv。
import arxiv
需要进行一些基础的配置,重点是搜索的关键字
# 配置参数
ARXIV_CONFIG = {
"save_dir": os.path.expanduser("./"), # 指定保存的位置
"categories": [
{
"name": "机器人操作",
"query": "(ti:\"robot manipulation\" OR abs:\"grasping\" OR abs:\"motion planning\") AND cat:cs.RO",
"max_results": 30
},
{
"name": "强化学习",
"query": "(abs:\"reinforcement learning\" OR abs:\"RL\" OR abs:\"Q-learning\") AND (cat:cs.LG OR cat:cs.AI)",
"max_results": 30
},
{
"name": "VLA模型",
"query": "(abs:\"vision-language-action\" OR abs:\"VLA\" OR abs:\"embodied AI\") AND (cat:cs.CV OR cat:cs.CL)",
"max_results": 30
}
],
"timezone_offset": 8, # 时区偏移(UTC+8)
"retries": 5, # API请求重试次数
"delay_seconds": 5 # 请求间隔
}
# 日志配置
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(os.path.join(
ARXIV_CONFIG["save_dir"], "arxiv.log")),
logging.StreamHandler()
]
)
接着创建请求客户端
def get_arxiv_client() -> arxiv.Client:
"""创建带重试机制的arXiv客户端"""
return arxiv.Client(
page_size=100,
delay_seconds=ARXIV_CONFIG["delay_seconds"],
num_retries=ARXIV_CONFIG["retries"]
)
客户端可以根据关键字来进行检索
def fetch_category_papers(category: Dict) -> List[arxiv.Result]:
"""抓取单个分类的最新论文"""
client = get_arxiv_client()
search = arxiv.Search(
query=category["query"],
max_results=category["max_results"],
sort_by=arxiv.SortCriterion.SubmittedDate
)
try:
papers = list(client.results(search))
logging.info(f"[{category['name']}] 获取到{len(papers)}篇论文")
return papers
except:
logging.error(f"[{category['name']}] API请求失败: {str(e)}")
return []
检索到的文章放到命名为 papers 的列表中。
翻译
脚本中使用了腾讯翻译、百度翻译、和小牛翻译,用免费的额度就应该够了。
from hashlib import md5
import urllib.error
import urllib.parse
import urllib.request
import requests
import random
from tencentcloud.common import credential
from tencentcloud.tmt.v20180321 import tmt_client, models
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
腾讯提供了封装好的库,其他两个用 request 来调用翻译的 API。
三种翻译服务的调用翻译的方式:
- 腾讯云翻译
def tengxun_translate_text(text: str) -> str:
try:
cred = credential.Credential(
"your id", "you key")
client = tmt_client.TmtClient(cred, "ap-beijing")
req = models.TextTranslateRequest()
req.SourceText = text
req.Source = "en"
req.Target = "zh"
req.ProjectId = 0
resp = client.TextTranslate(req)
return resp.TargetText
except Exception as e:
logging.error(f"腾讯翻译失败: {str(e)}")
return ""
- 百度翻译
def make_md5(s, encoding='utf-8'):
return md5(s.encode(encoding)).hexdigest()
def baidu_translate_text(text: str) -> str:
try:
# Set your own appid/appkey.
appid = 'your appid'
appkey = 'your appkey'
# For list of language codes, please refer to `https://api.fanyi.baidu.com/doc/21`
from_lang = 'en'
to_lang = 'zh'
endpoint = 'http://api.fanyi.baidu.com'
path = '/api/trans/vip/translate'
url = endpoint + path
query = text
# Generate salt and sign
salt = random.randint(32768, 65536)
sign = make_md5(appid + query + str(salt) + appkey)
# Build request
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
payload = {'appid': appid, 'q': query, 'from': from_lang,
'to': to_lang, 'salt': salt, 'sign': sign}
# Send request
r = requests.post(url, params=payload, headers=headers)
result = r.json()
# Show response
return result["trans_result"][0]["dst"]
except Exception as e:
logging.error(f"百度翻译失败: {str(e)}")
return ""
- 小牛翻译
def xiaoniu_translate_text(text: str) -> str:
url = 'http://api.niutrans.com/NiuTransServer/translation?'
apikey = "your apikey "
data = {"from": "en", "to": "zh", "apikey": apikey, "src_text": text}
data_en = urllib.parse.urlencode(data)
req = url + "&" + data_en
try:
res = urllib.request.urlopen(req)
res = res.read()
res_dict = json.loads(res)
return res_dict["tgt_text"]
except Exception as e:
logging.error(f"小牛翻译失败: {str(e)}")
return ""
总的翻译接口
def translate_text(text: str) -> str:
zh_abstract = tengxun_translate_text(text)
if zh_abstract == "":
zh_abstract = xiaoniu_translate_text(text)
if zh_abstract == "":
zh_abstract = baidu_translate_text(text)
return zh_abstract
要在各自的翻译服务控制台设置一下,如果额度用完了就禁用,这样就会选还有额度的服务,避免自动扣钱。
保存成 md 文件
创建文件夹
首先根据当天日期创建相应的目录路径,然后根据该路径创建文件夹
# 生成每日目录路径
def get_daily_dir() -> str:
local_date = datetime.now(
timezone(timedelta(hours=ARXIV_CONFIG["timezone_offset"]))).date()
return os.path.join(ARXIV_CONFIG["save_dir"], local_date.strftime("%Y%m%d"))
保存已检索的文章
创建一个 json 文件用来保存已检索的文章的 id
exist_papers = []
if os.path.exists(ARXIV_CONFIG["save_dir"]+"arxiv_papers.json"):
with open(ARXIV_CONFIG["save_dir"]+"arxiv_papers.json", 'r') as f:
papers_dict = json.load(f)
exist_papers = papers_dict["papers"]
else:
with open(ARXIV_CONFIG["save_dir"]+"arxiv_papers.json", 'w') as f:
json.dump({"papers": exist_papers}, f)
前面用 arxiv 的客户端检索了一个 papers 的列表,现在来对这个列表里面的文章进行处理。
如果文章已经在 json 文件里,直接跳过。如果没有,则整理它的信息,并放到已有文章的列表里
for paper in papers:
# 生成安全文件名
# paper_id = paper.entry_id.split('/')[-1]
paper_id = paper.get_short_id()
if paper_id in exist_papers:
logging.info(f"文献已存在 → {paper_id}")
continue
else:
exist_papers.append(paper_id)
这里先根据 arxiv 抓取的文章信息提取元数据,并整理成 md 的元数据。
safe_title = re.sub(r'[^\w_()()\-]', ' ', paper.title).strip(' ')[:200]
# filename = f"{paper_id}_{safe_title}.md"
filename = f"{safe_title}.md"
filepath = os.path.join(daily_dir, filename)
# 获取DOI(需要根据实际情况调整)
doi = getattr(paper, 'doi', []) or extract_doi_from_links(paper.links)
# 构建元数据
metadata = f"""---
title: "{paper.title}"
id: "{paper_id}"
authors: {[author.name for author in paper.authors]}
tags: {[tag.replace(".","/") for tag in paper.categories]}
category: {paper.primary_category}
doi: "{doi}"
url: "{paper.entry_id}"
published: "{paper.published.strftime('%Y-%m-%d')}"
update: "{paper.updated.strftime('%Y-%m-%d')}"
---
"""
再对摘要进行翻译
# 处理摘要翻译
en_abstract = paper.summary.replace('\n', ' ')
zh_abstract = ""
zh_abstract = translate_text(en_abstract)
if zh_abstract == "":
logging.error(f"翻译失败: {paper.title}")
time.sleep(5)
content = f"""{metadata}
## Abstract
{en_abstract}
{"## 摘要" if zh_abstract else ""}
{zh_abstract if zh_abstract else ""}
## PDF Links
- [arXiv PDF]({paper.pdf_url})
"""
最终将提取的信息写入文章名对应的 md 文件。并将已有文章的列表更新到 json 文件。
# 写入文件
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content.strip())
logging.info(f"文献保存 → {filepath}")
with open(ARXIV_CONFIG["save_dir"]+"arxiv_papers.json", 'w') as f:
json.dump({"papers": exist_papers}, f)
最终脚本地址归档
More Reading
Reference
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下
作者: pomolnc
出处: https://www.cnblogs.com/pomolnc/p/18799599
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。
若内容有侵犯您权益的地方,请公告栏处联系本人,本人定积极配合处理解决。

浙公网安备 33010602011771号