Loading...
墨滴

风雪夜归

2021/05/05  阅读:42  主题:默认主题

爬虫:交易所债券持有结构

爬虫:交易所债券持有结构

数据分享地址,貌似也没啥更好的方法了
百度云盘链接: https://pan.baidu.com/s/1TKlxCIKp8PuRYSwPWdyMkw
提取码: ptj9

爬虫最初级的就是静态网页,一个requests.get(url)啥都出来了,不过现在有交互数据的这类网页都快绝迹了。
其次就是动态网页,但url的参数和值都是有规律的,这样进入网页、F12找到真正的url和GET/POST方法,用requests包也是一条语句就解决了。

交易所反爬虫

但上交所网站的动态表单背后是执行js函数读取数据库,关键url地址里的callback函数名称后面带了随机数,所以实际的url是随机的,让获取实际url然后直接get失效!

selenium

没办法,最后用selenium包,模拟网页交互动作——

  1. 查看自己使用的浏览器软件和版本;
  2. 下载对应版本的webdriver;
  3. 进入网页,F12查找交互、查询元素的位置;
  4. selenium直接操作交互元素,模拟设置日期值、点击查询元素;
  5. 找到表单元素,偷懒直接用pandas的read_html读取表格;高手可以用beautifulsoup解析。

code

本来我以为都麻烦到用selenium了,应该马上就搞定了,结果还是有问题。

上交所的日期,设置月份时,如果是1月份,send_keys(1)实际效果是在1、10、11、12月循环;send_keys(11)一般是在当前为10月份时使用,实际效果是在10、12月循环,试了半天最后先设到9月份查一次,再设置11月份。有说是selenium的bug,有说是webdriver和浏览器版本不一致的。
结果,select_date()就如下臭长。
extract_data()本来也是一句话就搞定的,为了防止网页反应慢、或其他bug,加了sleep表头日期与查询日期是否一致的检查

import time
import pandas as pd
from selenium import webdriver

edge_driver = r"D:\msedgedriver.exe" # 改为自己电脑上的路径

class CrawlerSH():
    def __init__(self, url='http://bond.sse.com.cn/data/statistics/monthly/mainbond/'):
        self.driver = webdriver.Edge(edge_driver)
        self.url = url
    
    def go_to_page(self):
        self.driver.get(self.url)
    
    def click_button(self):
        btn = self.driver.find_element_by_class_name("search-btn")
        btn.click()

    def select_date(self, year, month):
        year, month = str(year), str(month)
        year_checkbox = self.driver.find_element_by_id('year')
        month_checkbox = self.driver.find_element_by_id('month')
        year_checkbox.send_keys(year)
        if month not in ['1''11']:
            month_checkbox.send_keys(month)
        elif month == '1':
            while True:
                month_checkbox.send_keys(month)
                if month_checkbox.get_attribute('value') == month:
                    break
        elif month == '11':
            while True:
                month_checkbox.send_keys('9')
                self.click_button()
                time.sleep(2)
                month_checkbox.send_keys(month)
                if month_checkbox.get_attribute('value') == month:
                    break
        return None

    def extract_data(self):
        time.sleep(2)
        header_date = self.driver.find_element_by_css_selector('#info').text
        header_date = header_date.split(':')[-1]
        header_date = header_date.replace('年''-').replace('月''')
        year = self.driver.find_element_by_id('year').get_attribute('value')
        month = self.driver.find_element_by_id('month').get_attribute('value')
        search_date = year + '-' + month
        if header_date != search_date:
            return None
        bond_df = pd.read_html(self.driver.page_source)[0]
        bond_df['日期'] = year + '-' + month.rjust(2'0')
        return bond_df

深交所的麻烦就是有些元素没法通过id、或者class_name获取位置,最后只能用巨长的css_selector或者xpath地址获取,万幸F12下能直接复制这个地址,不然眼睛都要找花了。

# 略import
class CrawlerSZ():
    def __init__(self, url='http://bond.szse.cn/marketdata/statistics/report/struc/index.html'):
        self.driver = webdriver.Edge(edge_driver)
        self.url = url
    
    def go_to_page(self):
        self.driver.get(self.url)

    def click_button(self):
        btn = self.driver.find_element_by_css_selector('body > div.g-container-wrap > div > div.g-content > div > div > div.report-query.mainquery-container.form-inline > div > div.form-group.form-condition1')
        btn.click()

    def select_date(self, year, month):
        year, month = str(year), str(month)
        month = month.rjust(2'0')
        date_checkbox_start = self.driver.find_element_by_id('zqxqcyyb_tab1_jyrqStart')
        date_checkbox_start.clear()
        date_checkbox_start.send_keys(year+'-'+month)
        return None

    def extract_data(self):
        time.sleep(2)
        header_date = self.driver.find_element_by_css_selector('body > div.g-container-wrap > div > div.g-content > div > div > div.report-content > div > div.report-table-head.clearfix.table-head-tab1 > div.table-title.pull-left > span.table-subname.table-subname-tab1').text
        header_date = header_date.replace('年','-').replace('月''')
        search_date = self.driver.find_element_by_id('zqxqcyyb_tab1_jyrqStart').get_attribute('value')
        if header_date != search_date:
            return None
        bond_df = pd.read_html(self.driver.page_source)[0]
        bond_df.columns = bond_df.columns.droplevel(0)
        bond_df['日期'] = header_date
        return bond_df

实际获取代码,比如2021年1月:

bot = CrawlerSH()
bot.go_to_page()
bot.select_date(20211)
bot.click_button()
df = bot.extract_data()

想获取历史数据,自己加个循环就可以了。

查找解决方案时,搜到以前各种爬交易所数据的爬虫,后来都失效了;所以估计用不了多久交易所又会改网页了。

风雪夜归

2021/05/05  阅读:42  主题:默认主题

作者介绍

风雪夜归