基于python+selenium+Chrome自动化爬取巨潮资讯网A股财务报表

转自同窗的博客

引言:css

网页爬虫分为静态网页爬虫和动态网页爬虫,前者是指索要获取的网页内容不须要通过js运算或者人工交互,python

后者是指获取的内容必需要通过js运算或者人工交互。这里的js运算多是ajax,人工交互不须要解释了。nginx

静态爬虫如今已经很成熟了,借助于python中的urllib和beautifulsoup能够很容易实现,爬到的内容通web

过python的字符串处理写入数据库,甚至能够经过web形式展示。动态爬虫有两种工具,一种是selenium,现ajax

在是selenium2(selenium+webdriver),另外一种是headless的phantomjs(对caperjs的封装),前者主要是chrome

经过控制浏览器实现,尤为是那种带video tag的场合,例如国内的一些CP站点例如youku,后者则是不须要数据库

展示内容的场合,或者能够理解为不带video tag的场合。听说后者的速度要比前者快,由于它不须要浏览器网页爬虫

展示,能够闷头去作。可是我只接触了了selenium这个工具……..数组

动态爬虫相对于静态爬虫,速度是要慢不少的。通常来讲,用静态爬虫方法爬不到的数据,咱们才采用动态爬虫工具爬取。 爬取的方法简单粗暴,和日常操做浏览器非常一个道理。浏览器

工具简介:

Selenium:是ThoughtWorks开发的一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操做同样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等,在python里面有第三方库,可直接安装使用。详细介绍见百度百科:https://baike.baidu.com/item/Selenium/18266?fr=aladdin

chrome:就是谷歌浏览器,一款功能强大且齐全的浏览器。

工具准备以及环境搭建:

python:首选Anaconda

    selenium:pip install selenium

关键就是用selenium驱动chrome: 首先在http://www.chromeliulanqi.com/下载32位版本的谷歌浏览器(注意是32位。64位貌似驱动不了)。而后下载驱动插件 连接:https://pan.baidu.com/s/1jIIjlwq 密码:xnl1

下载解压后,将chromedriver.exe 发到Python的安装目录,例如 D:\python 。 而后再将Python的安装目录添加到系统环境变量的Path下面

用下面的python代码简单测试一下:

from selenium import webdriver
browser = webdriver.Chrome()
browser.get('http://www.baidu.com/'

若是火狐浏览器自启并访问百度,则准备工做完成。

seleium的方法:

找了一篇相对详细具体的博客介绍http://blog.csdn.net/xfyangle/article/details/66478675,待会对目标网页分析的时候对具体方法说明一下。

开始爬取:

如下是巨潮资讯网的页面:
这里写图片描述

在我的财务数据那一栏,输入详细的公司代码,选择类别和年份,点击下载,浏览器就会下载并保存到本地了。咱们用selenium驱动谷歌浏览器的话,其实也是同样的操做。

from selenium import webdriver #导入类库
driver = webdriver.Chrome()  #实例化浏览器对象

输入公司代码:

这里写图片描述
首先把鼠标点进代码文本框,右击审查元素,会看到这个文本框的网页源代码

选择用id进行定位,并向文本框中输入公司代码:

driver.find_element_by_id('index_cw_input_obj').send_keys("公司代码")

在这个网站下载了一份包含全部公司代码的csv文件,将公司代码提取出来,保存在数组中,用for循环不断send就能够了:

import csv
with open("20171226091635.csv","r") as csvfile:
    reader = csv.reader(csvfile)
    for line in reader:
        ID.append(line[0][1:])
    ID=ID[2:]     #ID就是包含全部公司代码的数组

提交了代码以后,会出现下拉选择条

这里写图片描述

把鼠标移到下面,选择审查:

这里写图片描述

driver.find_element_by_name("公司代码").click() #经过name,也就是刚才在文本框中输入的公司代码,来定位,而后点击

选择类别:

把鼠标移到类别框,审查元素

这里写图片描述

对于这种下拉菜单,有相应的方法选择其中的值,

from selenium.webdriver.support.ui import Select
select = Select(driver.find_element_by_css_selector('#index_select_type_obj'))
#经过css选择器,根据id定位到下拉菜单
select.select_by_index(j) #,选择相应的项,若j=0,则选择利润表,若j=1,则选择资产表,而后用for循环遍历3种表。

选择年份(本次只须要选择左边的年份,右边的默认当前年份):

同上:审查元素以后,根据id定位

select2 = Select(driver.find_element_by_id('cw_start_select_obj')) #经过id定位到年份
select2.select_by_index(j) #默认选择最老的年份,j=0

下载:

把鼠标移到下载按钮,点审查元素:
这里写图片描述

对于这个元素的定位,很奇怪,用id,name,xpath都不行,只能用css selector。对于css选择器以及xpath,咱们很差直接知道他们具体的值,咱们采用:对当前网页源代码这一行(如上图),右击,选择copy,而后选择copy selector 或者copy xpath,而后将复制的值,填到相应的方法的括号参数里面,便可定位。

driver.find_element_by_css_selector('#con-f-2 > div > div.down_button_row > button').click() #用css selector定位,而后用.click()方法点击改按钮,便可下载在你启动的浏览器中

这就是大概的一个流程,除了上述所说的过程以外。在两步操做之间,要设定必定的时间间隔,让浏览器反应一下,若浏览器没有加载出来,就会出现找不到元素的错误。以及在大量下载数据的过程当中,每次下载必定数量的文件以后,网站会要求输入验证码才能下载(可是每次都输不对,过段时间又能够下载)。这就须要巧妙的用sleep函数设置休息间隔了。

代码实现:

from selenium import webdriver
from selenium.webdriver.support.ui import Select
import time
import csv
count=1 #下载计数
relax=1  #休息计数
ID=[]  #公司代码
NAME=[]  #公司名称
with open("20171226091635.csv","r") as csvfile:
    reader = csv.reader(csvfile)
    for line in reader:
        ID.append(line[0][1:])
        NAME.append(line[1])
    ID=ID[2:]
    NAME = NAME[2:]
driver = webdriver.Chrome()
driver.get("http://www.cninfo.com.cn/cninfo-new/index#")  #打开页面
time.sleep(5)   #等待5s
for i in ID:  #遍历全部的公司代码
    while (relax % 60) == 0:  #下载60个公司的报表,也就是点击下载180次,休息300s
        time.sleep(300)
        relax = relax + 1
    while(relax%40)==0:    #下载40个公司的报表,也就是点击下载130次,休息120s 
        time.sleep(120)
        relax=relax+1
    while(relax%20)==0:   #下载20个公司的报表,也就是点击下载60次,休息60s
        time.sleep(60)
        relax=relax+1
    driver.find_element_by_id('index_cw_input_obj').send_keys(i)   #向文本框提交公司代码
    driver.find_element_by_name(i).click() #选择相应的代码,点击
    print("正在下载第" + str(count) + "个: 代码: " + ID[count - 1] + "名称: " + NAME[count - 1]+".....")   #可视化下载的过程
    for j in range(0,3):     #每一个公司又3个表
        select1 = Select(driver.find_element_by_css_selector('#index_select_type_obj'))
        select1.select_by_index(j)   #选择相应的表
        select2 = Select(driver.find_element_by_id('cw_start_select_obj'))
        select2.select_by_index(0) #选择最久的年份
        driver.find_element_by_css_selector('#con-f-2 > div > div.down_button_row > button').click()    #点击下载
        time.sleep(3)   #等待3s
    print("下载成功")    
    driver.find_element_by_id('index_cw_input_obj').clear() #将代码框中的代码清除
    count=count+1  #加一
    relax=relax+1   #加一
    time.sleep(2)   #下载完一个休息2s
print("下载完成")

本地处理:

下载的zip文件是一个一个散的,很无序,并且都没有解压。如图:

这里写图片描述

首先位每一个公司建立目录,名称为公司代码_公司名称。而后在每一个公司目录下面建立3个目录,以此为lrb,fzb,llb。

import os  #里面有建立文件夹的方法
import csv
count=0  #计数
ID=[]  #公司代码
NAME=[] #公司名称
with open("20171226091635.csv","r") as csvfile:
    reader = csv.reader(csvfile)
    for line in reader:
        ID.append(line[0][1:])
        NAME.append(line[1])
    ID=ID[2:]   
    NAME = NAME[2:]  
os.mkdir("A股报表大全") #建立子目录
for x in ID:
    filename=x+'_'+NAME[count]  #将公司代码以及名称拼接起来
    if '*' in filename:
        filename=filename.replace("*","")  #把公司名称里面的*去掉,否则建立文件时会提示非法字符
    os.mkdir("A股报表大全/" + filename)    # (‘/’表明改目录的下一个目录)在A股报表大全目录下建立以公司代码_公司名称为名字的目录
    os.mkdir("A股报表大全/"+ filename  + "/" + "lrb")  #在公司目录下分别建立3个子目录
    os.mkdir("A股报表大全/" + filename + "/" + "fzb")
    os.mkdir("A股报表大全/" + filename + "/" + "llb")
    count=count+1

这里写图片描述

接下来就是把zip解压,而后放到对应的文档里面

import zipfile  #解压缩zip的库
import os
import csv
file_list = os.listdir(r'A股表压缩包大全.') 
ID=[] #以 公司代码,公司名的形式循环存放
name="" #公司名称
table_tag="" #标记zip是什么类型的报表
file_dir=""
with open("20171226091635.csv","r") as csvfile:
    reader = csv.reader(csvfile)
    for line in reader:
        ID.append(line[0][1:])
        ID.append(line[1])
ID=ID[2:]
for file_name in file_list: #遍历压缩包目录
    if os.path.splitext(file_name)[1] == '.zip':
        print(file_name)
        count = 0
        id=file_name[7:13]  #在zip压缩包的文件名中取出公司代码
        table_tag=file_name[3:6]  #在zip压缩包的文件名中取出公司名称
        for i in ID:
            if i==id:
                name=ID[count+1]
                break
            count=count+1
    file_dir=str(id)+'_'+str(name) #为zip压缩包找到他对应的目录名
    if '*' in file_dir:
        file_dir=file_dir.replace("*","") #去掉*
    if table_tag=="lrb":  #若是是lrb表
        filedir="A股报表大全/"+file_dir + "/" + "lrb"
        file_zip = zipfile.ZipFile("A股表压缩包大全/"+ file_name)  #解压压缩包
        for file in file_zip.namelist(): #遍历压缩包里的文件
            file_zip.extract(file, filedir) #将file移动到filedir里面
        file_zip.close()  #关闭zip文件
    if table_tag=="fzb":
        filedir = "A股报表大全/" + file_dir + "/" + "fzb"
        file_zip = zipfile.ZipFile("A股表压缩包大全/"+ file_name)
        for file in file_zip.namelist():
            file_zip.extract(file, filedir)
        file_zip.close()
    if table_tag=="llb":
        filedir = "A股报表大全/" + file_dir + "/" + "llb"
        file_zip = zipfile.ZipFile("A股表压缩包大全/"+ file_name)
        for file in file_zip.namelist():
            file_zip.extract(file, filedir)
        file_zip.close()

自此, 完整的A股财务报表大全就有序的整理好了,可下载看看百度云连接

这里写图片描述

总结:因为每一次爬取次数的限制,以及驱动谷歌浏览器所出现的有时响应缓慢的问题,再加上一些未知错误(莫名的程序就停下来了)。本次爬取花了大概4天的时间将3455家公司,10365个zip文件爬下来,能够说要很是的有耐心了。代码不是关键,耐心才是重点。

以上如有不详细之处,可自行百度,或者同我交流。