从巨潮网批量爬取三板公司年报

1.在b站和CSDN找到很多批量爬取年报的方法。我找到的大部分代码不区分公告类型,但是我只需要年报。

2.我想要的是三板挂牌公司的年度报告,我找到的方法下载的都是主板上市公司的公告。怎样将参数中的证券类型替换为三板也难倒了我。自学摸索,终于在开发人员工具中找到巨潮的三板的参数

"column": "third",
"category": "category_dqgg"

3.我不需要年度报告摘要,或英文、或半年度报告等不符合我下载需要的文件。在B站找到一个视频链接,设置了下载条件。奇怪的是按照up主的代码是可以下载主板上市公司公告的,换成三板的参数,下载的pdf都只有1kb,打不开的。

python爬取巨潮资讯上市公司年报_哔哩哔哩_bilibili

4.找到这个链接,很简洁的代码,关键是真的管用。我把b站up主设定的下载条件添加到这个链接提供的代码里。三板的年度报告终于可以批量下载了。

【工作提效】教你如何用Python轻松爬取上市公司年报,新手也能快速上手!copy即用_教你如何用python轻松爬取上市公司年报,新手也能快速上手-CSDN博客

以下是下载2023-11-01~2024-06-30日的新三板挂牌公司年度报告的源代码。

import requests
import pandas as pd
import os

# 请求头
headers = {
    "accept": "*/*",
    "accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
    "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
    "proxy-connection": "keep-alive",
    "x-requested-with": "XMLHttpRequest"
}

# 指定完整路径
base_path = 'D:\\PycharmProjects\\pythonProject1\\Report_2023'
announcements_dir = os.path.join(base_path, 'announcements')
# 创建存储PDF文件的文件夹
os.makedirs(announcements_dir, exist_ok=True)


# 获取公告数据的函数
def fetch_announcements(start_date, end_date, page_num=1, page_size=30):
    url = "http://www.cninfo.com.cn/new/hisAnnouncement/query"
    body = {
        "pageNum": page_num,
        "pageSize": page_size,
        "column": "third",
        "tabName": "fulltext",
        "plate": "neeq",
        "stock": "",
        "searchkey": "",
        "secid": "",
        "category": "category_dqgg",
        "trade": "农、林、牧、渔业;电力、热力、燃气及水生产和供应业;交通运输、仓储和邮政业;科学研究和技术服务业;教育;综合;卫生和社会工作;"
                 "水利、环境和公共设施管理业;住宿和餐饮业;建筑业;采矿业;文化、体育和娱乐业;居民服务、修理和其他服务业;租赁和商务服务业;房"
                 "地产业;信息传输、软件和信息技术服务业;批发和零售业;制造业",
        "seDate": f"{start_date}~{end_date}",
        "sortName": "",
        "sortType": "",
        "isHLtitle": "true"
    }

    # 发送 POST 请求
    response = requests.post(url, headers=headers, data=body)

    if response.status_code == 200:
        data = response.json()
        if 'announcements' in data:
            announcements_list = []
            for announcement in data['announcements']:
                sec_code = announcement.get("secCode", "")
                sec_name = announcement.get("secName", "")
                title = announcement.get("announcementTitle", "")
                download_url = f"http://static.cninfo.com.cn/{announcement.get('adjunctUrl', '')}"

                # 筛选条件:只保留年度报告,且排除包含“公告”、“摘要”、“已取消”和“英文”的标题
                if ('年度报告' in title) and ('公告' not in title) and ('摘要' not in title) and (
                        '已取消' not in title) and ('英文' not in title) and ('半年度' not in title):
                    announcements_list.append([sec_code, sec_name, title, download_url])
            print(f"Fetched {len(announcements_list)} announcements. hasMore is {data['hasMore']}")
            return announcements_list, data['hasMore']
        else:
            print("No announcements found that match the criteria.")
            return [], False
    else:
        print(f"Request failed with status code: {response.status_code}")
        return [], False


# 下载PDF文件的函数
def download_pdf(url, filename):
    response = requests.get(url)
    if response.status_code == 200:
        with open(filename, 'wb') as f:
            f.write(response.content)
        print(f"Downloaded: {filename}")
    else:
        print(f"Failed to download: {filename}, Status code: {response.status_code}")


# 主函数
def main():
    try:
        start_date = "2023-11-01"  # 注意日期格式,原代码中的日期可能是错误的(月份和日期颠倒)
        end_date = "2024-06-30"
        page_size = 30
        page_num = 1
        all_announcements = []

        while True:
            announcements, has_more = fetch_announcements(start_date, end_date, page_num=page_num, page_size=page_size)
            if announcements:
                all_announcements.extend(announcements)
                if not has_more:
                    break
                page_num += 1
            else:
                break

        if all_announcements:
            # 使用 pandas 创建 DataFrame
            df = pd.DataFrame(all_announcements, columns=["公司代码", "公司名称", "年报标题", "下载地址"])

            # 将 DataFrame 保存到 Excel 文件中
            excel_path = os.path.join(base_path, "announcements.xlsx")
            df.to_excel(excel_path, index=False)
            print(f"数据已保存到 {excel_path} 文件中")

            # 下载 PDF 文件
            for announcement in all_announcements:
                sec_code, sec_name, title, download_url = announcement
                # 进一步处理标题以避免文件名冲突
                safe_title = title.replace("/", "_").replace("\\", "_").replace(":", "_").replace("*", "_").replace("?",
                                                                                                                    "_").replace(
                    "\"", "_").strip()
                filename = f"{announcements_dir}\\{sec_code}_{sec_name}_{safe_title}.pdf"
                download_pdf(download_url, filename)
    except Exception as e:
        print(f"发生错误:{e}")


# 执行主函数
if __name__ == "__main__":
    main()

<think>好的,我现在要处理用户关于使用爬虫从巨潮资讯网抓取年报的问题。首先,我需要理解用户的具体需求。他们提到了使用Python、Scrapy和Selenium,所以应该需要结合这些工具的技术方法。 根据用户提供的引用内容,特别是引用[2]和[3],里面有提到用PhantomJS+Selenium+Scrapy来抓取巨潮资讯网的信息。这意味着用户可能已经看过这些资料,但需要更详细的指导。我需要整理这些信息,给出一个结构化的回答,涵盖技术选型、步骤、代码示例以及注意事项。 接下来,我要确认技术方案。Scrapy适合处理结构化爬取,但巨潮资讯网可能有动态加载的内容,所以需要Selenium来渲染JavaScript。同时,年报可能是PDF文件,需要考虑如何下载和处理这些文件。引用中提到处理动态加载数据、分页和验证码,这些都需要在步骤中说明。 然后,我需要规划回答的结构。从技术选型开始,说明为什么选择这些工具,然后是具体步骤,包括分析页面、模拟登录、处理分页、提取链接、下载PDF和存储数据。代码示例部分需要展示Scrapy和Selenium的结合使用,比如中间件和Spider的编写。 还要注意用户提到的反爬机制,比如频率限制和验证码,需要给出应对策略,比如设置下载延迟和使用OCR库。此外,存储部分需要建议多种方式,如本地存储、数据库或云存储,这样用户可以根据需求选择。 最后,生成相关问题时要围绕技术难点、优化方法、法律风险等方面,帮助用户进一步探索。同时确保所有引用标注正确,特别是引用[2]和[3]的内容,在相关部分自然添加标识符。</think>### 从巨潮资讯网抓取年报的技术方法 #### 技术选型与核心思路 巨潮资讯网(www.cninfo.com.cn)采用动态加载技术,年报数据通过JavaScript渲染生成,因此需要结合以下技术: 1. **Scrapy框架**:用于构建爬虫主架构,处理请求调度和数据管道[^1] 2. **Selenium/Playwright**:模拟浏览器操作,解决动态渲染问题[^2] 3. **PyPDF2/pdfplumber**:用于后续处理下载的PDF年报文件 #### 具体实现步骤 1. **页面结构分析** - 使用Chrome开发者工具(F12)分析年报列表加载方式 - 发现数据接口为`http://www.cninfo.com.cn/new/hisAnnouncement/query` - 请求方式为POST,参数包含`stock`、`pageNum`等关键字段[^3] 2. **模拟登录与请求头设置** ```python # Scrapy中间件示例 from selenium import webdriver class SeleniumMiddleware: def __init__(self): options = webdriver.ChromeOptions() options.add_argument('--headless') self.driver = webdriver.Chrome(options=options) def process_request(self, request, spider): if request.meta.get('selenium'): self.driver.get(request.url) return HtmlResponse(url=self.driver.current_url, body=self.driver.page_source, encoding='utf-8', request=request) ``` 3. **分页处理逻辑** ```python # Spider核心代码 class CninfoSpider(scrapy.Spider): name = 'cninfo' custom_settings = { 'DOWNLOAD_DELAY': 3, 'SELENIUM_REQUESTS': True } def start_requests(self): formdata = { 'stock': '000001', 'tabName': 'fulltext', 'pageSize': 30, 'pageNum': 1 } yield scrapy.FormRequest( url='http://www.cninfo.com.cn/new/hisAnnouncement/query', formdata=formdata, meta={'selenium': True} ) ``` 4. **PDF链接提取与下载** ```python # 解析年报下载链接 def parse(self, response): data = json.loads(response.text) for item in data['announcements']: pdf_url = f"http://www.cninfo.com.cn/{item['adjunctUrl']}" yield { 'title': item['announcementTitle'], 'date': item['announcementTime'], 'pdf_url': pdf_url } yield scrapy.Request(pdf_url, callback=self.save_pdf) # PDF保存处理 def save_pdf(self, response): filename = response.url.split('/')[-1] with open(f'pdfs/{filename}', 'wb') as f: f.write(response.body) ``` #### 反爬应对策略 1. **IP频率限制**:设置`DOWNLOAD_DELAY`(建议≥3秒) 2. **验证码识别**:使用`django-simple-captcha`或第三方OCR服务 3. **请求头随机化**:通过`fake_useragent`库动态生成User-Agent #### 数据存储建议 - 本地存储:PDF文件+元数据CSV - 数据库存储:MongoDB(适合非结构化数据) - 云存储:阿里云OSS/AWS S3(大文件存储场景)
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值