From 606cad43dc103c31e201d497024ac2b818a1b093 Mon Sep 17 00:00:00 2001 From: yuyx <237899745@qq.com> Date: Wed, 14 Jan 2026 13:24:29 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=97=A0=E9=99=84?= =?UTF-8?q?=E4=BB=B6=E6=96=87=E7=AB=A0=E6=97=A0=E6=B3=95=E6=A0=87=E8=AE=B0?= =?UTF-8?q?=E5=B7=B2=E8=AF=BB=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 发现标记已读的正确 API: /tools/submit_ajax.ashx?action=saveread - 添加 mark_article_read 方法调用 saveread API 标记文章已读 - 修改 get_article_attachments 返回文章的 channel_id 和 article_id - 对每篇文章都调用 mark_article_read,无论是否有附件 - 解决米米88、黄紫夏99等账号文章无法标记已读的问题 Co-Authored-By: Claude Opus 4.5 --- api_browser.py | 61 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/api_browser.py b/api_browser.py index 21d75b7..2b685d5 100755 --- a/api_browser.py +++ b/api_browser.py @@ -351,7 +351,14 @@ class APIBrowser: return articles, total_pages, next_page_url def get_article_attachments(self, article_href: str): - """获取文章的附件列表""" + """ + 获取文章的附件列表和文章信息 + + Returns: + tuple: (attachments_list, article_info) + - attachments_list: 附件列表 + - article_info: 包含 channel_id 和 article_id 的字典,用于标记文章已读 + """ if not article_href.startswith('http'): url = f"{BASE_URL}/admin/{article_href}" else: @@ -361,6 +368,16 @@ class APIBrowser: soup = BeautifulSoup(resp.text, 'html.parser') attachments = [] + article_info = {'channel_id': None, 'article_id': None} + + # 从 saveread 按钮获取 channel_id 和 article_id + for elem in soup.find_all(['button', 'input']): + onclick = elem.get('onclick', '') + match = re.search(r'saveread\((\d+),(\d+)\)', onclick) + if match: + article_info['channel_id'] = match.group(1) + article_info['article_id'] = match.group(2) + break attach_list = soup.find('div', {'class': 'attach-list2'}) if attach_list: @@ -383,10 +400,31 @@ class APIBrowser: }) break - return attachments + return attachments, article_info + + def mark_article_read(self, channel_id: str, article_id: str) -> bool: + """通过 saveread API 标记文章已读""" + if not channel_id or not article_id: + return False + + import random + saveread_url = f"{BASE_URL}/tools/submit_ajax.ashx?action=saveread&time={random.random()}&fl={channel_id}&id={article_id}" + + try: + resp = self._request_with_retry("post", saveread_url) + # 检查响应是否成功 + if resp.status_code == 200: + try: + data = resp.json() + return data.get('status') == 1 + except: + return True # 如果不是 JSON 但状态码 200,也认为成功 + return False + except: + return False def mark_read(self, attach_id: str, channel_id: str = '1') -> bool: - """通过访问预览通道标记已读""" + """通过访问预览通道标记附件已读""" download_url = f"{BASE_URL}/tools/download2.ashx?site=main&id={attach_id}&channel_id={channel_id}" try: @@ -499,9 +537,9 @@ class APIBrowser: new_articles_in_page += 1 title = article['title'][:30] - # 获取附件(文章详情页) + # 获取附件和文章信息(文章详情页) try: - attachments = self.get_article_attachments(article_href) + attachments, article_info = self.get_article_attachments(article_href) consecutive_failures = 0 except Exception as e: skipped_items += 1 @@ -516,12 +554,25 @@ class APIBrowser: total_items += 1 report_progress() + # 标记文章已读(调用 saveread API) + article_marked = False + if article_info.get('channel_id') and article_info.get('article_id'): + article_marked = self.mark_article_read( + article_info['channel_id'], + article_info['article_id'] + ) + + # 处理附件(如果有) if attachments: for attach in attachments: if self.mark_read(attach['id'], attach['channel_id']): total_attachments += 1 self.log(f"[API] [{total_items}] {title} - {len(attachments)}个附件") + else: + # 没有附件的文章,只记录标记状态 + status = "已标记" if article_marked else "标记失败" + self.log(f"[API] [{total_items}] {title} - 无附件({status})") time.sleep(0.1)