一文搞懂如何用Python爬取上市公司信息

Python爬取上市公司信息

1. 概念准备

  • Python基本概念
tb.to_csv(r'1.csv', mode='a', encoding='utf_8_sig', header=1, index=0)

r意思是强制不转义字符串

  • T a b l e Table 型表格
<table> 定义表格
<thead> 定义表格的页眉
<tbody> 定义表格的主体
<tr> 定义表格的行
<th> 定义表格的表头
<td> 定义表格单元
  • D a t a F r a m e . r e a d _ h t m l DataFrame.read\_html 函数

官网文档 IO各种方式

pandas.read_html(io, match='.+', flavor=None, header=None, index_col=None, skiprows=None, attrs=None, parse_dates=False, tupleize_cols=None, thousands=', ', encoding=None, decimal='.', converters=None, na_values=None, keep_default_na=True, displayed_only=True)
io u r l url h t m l html 本地文件等
flavor 解析器
header 标题行
skiprows 跳过的行
attrs 属性
parse_dates 解析日期
  • p a n d a s . D a t a . t o _ c s v ( ) pandas.Data.to\_csv() 函数

官方文档 示例讲解 中英文释

DataFrame.to_csv(path_or_buf=None, sep=', ', na_rep='', float_format=None, columns=None, header=True, index=True, index_label=None, mode='w', encoding=None, compression=None, quoting=None, quotechar='"', line_terminator='\n', chunksize=None, tupleize_cols=None, date_format=None, doublequote=True, escapechar=None, decimal='.')

初级教程

  1. install/remove of the service denied 管理员权限
  2. 启动:–>G: -->cd G:\MySQL\mysql-8.0.13-winx64\bin -->mysql -u root -p -->123456
  3. 创建\查看\选择\删除数据库

创建数据库–>create database db_name;

查看已有数据库–>show databases;

选择数据库–>use db_name;

删除数据库–>drop databse db_name;

当前数据库下所有表的名字–>show tables; 或者–>show tables from db_name;

查看表结构–>desc tb_name; -->describe tb_name; -->show columns from tb_name; —>show create table tb_name

  1. user与授权

*.* 指所有数据库,%指所有ip地址

查询mysql的user表–>select user,host from mysql.user;

创建用户–>CREATE USER ‘user_name’@‘host_address’ IDENTIFIED BY ‘password’;
–> insert into mysql.user(Host,User,Password)values(“localhost”,“name”,password(“12”));

删除用户–>delete from user where user=‘user_name’ and host=‘localhost’ ; -->flush privileges;
—>drop user ‘user_name’@‘host’;

赋予用户所有权限–>create user ‘root’@’%’ identified by ‘root’;
--------------------------->grant all provoleges on . to ‘root’@’%’ with grant option;

取消用户所有权限–>REVOKE privilege ON databasename.tablename FROM ‘username’@‘host’;

更改密码,其他方法都不行.–>ALTER USER ‘root’@‘localhost’ IDENTIFIED WITH mysql_native_password BY ‘你的密码’;

GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION;
flush privileges;
#上面是以前版本的语法,下面才是MySQL8.0的语法
CREATE USER 'root'@'%' IDENTIFIED BY 'root';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
  1. 常见错误

net start mysql 发生系统错误2。mysqld需要安装在bin目录下,先删除之前的服务mysqld --remove,然后在bin下重新安装mysqld install。

net stop mysql 发生系统错误 5。访问权限问题,切换到管理员权限。

ERROR 1049, "Unknown database ‘wade’。无法连接到数据库,检查连接设置。

ERROR 1064,语法问题。MySQL版本升级后很多语法都变换了格式。

(pymysql.err.OperationalError) (1045, “Access denied for user ‘root’@‘localhost’ (using password: YES)”) 通过pymysql存储数据到MySQL中出现。1.常规资料只能解决密码错误,但是我这里密码没有问题(结果只是这里,但不是关注的那段代码,而是另外的地方用到了)。2.phpwamp软件自身有mysql服务,可能会占用mysql的端口(只能开一个),打开服务,删掉多余的。3.数据库权限问题,未给root用户分配所有权限,本地客户端无权登录root用户。给root用户赋权限时,注意语法问题。三种方法都没有解决问题。

ERROR 2003: Can’t connect to MySQL server on ‘localhost’ (10061)。MySQL服务关闭了。在管理员权限下打开cmd–>net start mysql 或者–>cmd–>services.msc–>手动开启MySQL服务

代码是很严谨的,如果一个地方检测确实没有问题,那就确实不是这里的问题,而是其他地方,不存在可能之类的倾向。

用于检测 t r y try 语句块中的错误,从而让 e x c e p t except 语句捕获异常信息并处理。

try:
     Normal execution block
except A:
     Exception A handle
except B:
     Exception B handle
except:
     Other exception handle
else:
     if no exception,get here
finally:
     print("finally")   

e x c e p t except 可以直接理解为“if…成立并运行。

一个小游戏,让用户猜数字,提示“大了”“小了”,直到用户猜对。(代码可完美运行)

from random import randint
 
target = randint(0,100)
while 1:
    guess = input('plz guess a interger between 0 and 100:')
    try:
        num = int(guess)
    except:
        print('plz input an INTERGER!!')
    else:
        break
time = 1
while num != target:
    if num < target:
        print('smaller')
    elif num > target:
        print('bigger')
    while 1:
        guess = input('guess again...')
        try:
            num = int(guess)
        except:
            print('plz input an INTERGER!!')
        else:
            break
    time = time + 1
else:
    print('you win...you get the number in ',time,' steps!!')
  • URL中特殊符号意义

%s 格式化字符串(示例1 示例2)

%s 字符串 %b 二进制整数 %% %
%d 十进制整数 %o 八进制整数
  • N a v i c a t Navicat 简介

官方文档

Navicat是数据库图形管理工具,支持MySQL、MariaDB、SQL Server、SQLite、Oracle和PostgreSQL数据库。Navicat Premium是全系列版本,Navicat for mysql只支持MySQL。

  • B e a u t i f u l S o u p BeautifulSoup 选择器

官方文档 极客学院 四大选择器总结

BeautifulSoup支持 H T M L HTML l x m l lxml H t m l 5 l i b Html5lib 解析器

BeautifulSoup将复杂的HTML文档转换成一个便于阅读的树形结构化文件,每个节点都是Python对象,共4种:

  1. Tag
<title>The Dormouse's story</title>
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>

类似HTML和XML中的 t a g tag 。有两个属性name和attributes。

  1. NavigableString
  2. BeautifulSoup
  3. Comment:一个特殊类型的NavigableString对象

官方文档 非结构化数据 崔庆才XPath语法与lxml库

具有自动修正 H T M L HTML 代码的功能。

result = html.xpath('//li') #获取所有<li>标签
  • H T M L ( H y p e r T e x t M a r k u p L a n g u a g e ) HTML(HyperTextMarkupLanguage) 超文本标记语言

维基百科 W3school

一种创建网页的标准标记语言;常与 C S S CSS J a v a S c r i p t JavaScript 一起使用;标记语言而非编程语言。用来显示数据,焦点在数据的外观。而 X M L XML 被设计用来传输和存储数据。

X H T M L XHTML 是更严谨更纯净的 H T M L HTML 版本。

  • X M L ( E x t e n s i b l e M a r k u p L a n g u a g e ) XML(ExtensibleMarkupLanguage) 扩展标记语言

W3school

是一种类似于 H T M L HTML 的标记语言,被设计用来结构化(树结构,具有父兄子元素)、传输、存储数据,焦点在数据的内容,具有自我描述性,而非显示数据。标签需要自定义,没有预定义,是W3C的推荐标准。

X M L XML 仅仅是纯文本,文档本身不做任何操作,只是结构化、传输、存储信息,其发送、显示都需要有专门程序。可以理解为, X M L XML 是独立于软硬件的传输工具。

所有元素必须关闭标签。标签区分大小写。且必须有根元素。

文档中的空格会全部保留。

X S L T XSLT 是首选的 X M L XML 样式语言,而非 C S S CSS

可以拥有属性,但是属性值必须加引号。

#特殊符号需要实体引用
&lt==<   &gt==>   &amp==&   &apos=='   &quot=="
#注释
<!--this is a comment-->
  • X P a t h XPath X M L XML 中查找信息的语言。

W3school

X P a t h XPath 中有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释、文档(根)节点。 X M L XML 被当做节点树来对待。树的根被称为文档节点或者根节点。

2. 过程详解

  • U R L URL 解析

http://s.askci.com/stock/a/?reportTime=2017-12-31&pageNum=1#QueryCondition

通过删除#QueryCondition发现不影响打开网页,调整pageNum属性值确定不同页面,因此确定:

http://s.askci.com/stock/a/?reportTime=2017-12-31&pageNum=i

为构造 u r l url 。并注意:

a a 表示A股, a a 换成 h h 就是港股, a a 换成 x s b xsb 就是新三板,因此在网址分页 f o r for 外再构建一个 f o r for 循环就能爬取这三个股市的股票。

  • 快速抓取
import pandas as pd
import csv

for i in range(1,178):  # 爬取全部177页数据
    url = 'http://s.askci.com/stock/a/?reportTime=2017-12-31&pageNum=%s' % (str(i))
    tb = pd.read_html(url)[3] #经观察发现所需表格是网页中第4个表格,故为[3]
    tb.to_csv(r'1.csv', mode='a', encoding='utf_8_sig', header=1, index=0)
    print('第'+str(i)+'页抓取完成')
  • 网页提取函数
def get_one_page(i):
    try:
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'
        }
        paras = {
        'reportTime': '2017-12-31',   
        #可以改报告日期,比如2018-6-30获得的就是该季度的信息
        'pageNum': i   #页码
        }
        url = 'http://s.askci.com/stock/a/?' + urlencode(paras)
        response = requests.get(url,headers = headers)
        if response.status_code == 200:
            return response.text
        return None
    except RequestException:
        print('爬取失败')
  • 内容解析函数
def parse_one_page(html):
    soup = BeautifulSoup(html,'lxml')
    content = soup.select('#myTable04')[0] #[0]将返回的list改为bs4类型
    tbl = pd.read_html(content.prettify(),header = 0)[0]
    # prettify()优化代码,[0]从pd.read_html返回的list中提取出DataFrame

    tbl.rename(columns = {'序号':'serial_number', '股票代码':'stock_code', '股票简称':'stock_abbre', '公司名称':'company_name', '省份':'province', '城市':'city', '主营业务收入(201712)':'main_bussiness_income', '净利润(201712)':'net_profit', '员工人数':'employees', '上市日期':'listing_date', '招股书':'zhaogushu', '公司财报':'financial_report', '行业分类':'industry_classification', '产品类型':'industry_type', '主营业务':'main_business'},inplace = True)

    print(tbl)
    # return tbl
    # rename将表格15列的中文名改为英文名,便于存储到mysql及后期进行数据分析
    # tbl = pd.DataFrame(tbl,dtype = 'object') #dtype可统一修改列格式为文本
  • 主函数
# 主函数
def main(page):
    for i in range(1,page):   # page表示提取页数
        html = get_one_page(i)
        parse_one_page(html)

# 单进程
if __name__ == '__main__':    
    main(178)   #共提取n页
  • 创建在数据库中存放数据的表格
import pymysql

def generate_mysql():
    conn = pymysql.connect(
        host='localhost',   # 本地服务器
        user='root',
        password='******',  # 你的数据库密码
        port=3306,          # 默认端口
        charset = 'utf8',
        db = 'wade')
    cursor = conn.cursor()

    sql = 'CREATE TABLE IF NOT EXISTS listed_company2 (serial_number INT(30) NOT NULL,stock_code INT(30) ,stock_abbre VARCHAR(30) ,company_name VARCHAR(30) ,province VARCHAR(30) ,city VARCHAR(30) ,main_bussiness_income VARCHAR(30) ,net_profit VARCHAR(30) ,employees INT(30) ,listing_date DATETIME(0) ,zhaogushu VARCHAR(30) ,financial_report VARCHAR(30) , industry_classification VARCHAR(255) ,industry_type VARCHAR(255) ,main_business VARCHAR(255) ,PRIMARY KEY (serial_number))'
    # listed_company是要在wade数据库中建立的表,用于存放数据

    cursor.execute(sql)
    conn.close()

generate_mysql()
  • 将数据写入表格
import pymysql
from sqlalchemy import create_engine

def write_to_sql(tbl, db = 'wade'):
    engine = eate_engine('mysql+pymysql://root:******@localhost:3306/{0}?charset=utf8'.format(db))
    # db = 'wade'表示存储到wade这个数据库中,root后面的*是密码
    try:
        tbl.to_sql('listed_company',con = engine,if_exists='append',index=False)
        # 因为要循环网页不断数据库写入内容,所以if_exists选择append,同时该表要有表头,parse_one_page()方法中df.rename已设置
    except Exception as e:
        print(e)
  • 完整代码
import requests
import pandas as pd
from bs4 import BeautifulSoup
from lxml import etree
import time
import pymysql
from sqlalchemy import create_engine
from urllib.parse import urlencode  # 编码 URL 字符串

start_time = time.time()  #计算程序运行时间

def get_one_page(i):
    try:
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'
        }
        paras = {
        'reportTime': '2017-12-31',
        #可以改报告日期,比如2018-6-30获得的就是该季度的信息
        'pageNum': i   #页码
        }
        url = 'http://s.askci.com/stock/a/?' + urlencode(paras)
        response = requests.get(url,headers = headers)
        if response.status_code == 200:
            return response.text
        return None
    except RequestException:
        print('爬取失败')


def parse_one_page(html):
    soup = BeautifulSoup(html,'lxml')
    content = soup.select('#myTable04')[0] #[0]将返回的list改为bs4类型
    tbl = pd.read_html(content.prettify(),header = 0)[0]
    # prettify()优化代码,[0]从pd.read_html返回的list中提取出DataFrame
    tbl.rename(columns = {'序号':'serial_number', '股票代码':'stock_code', '股票简称':'stock_abbre', '公司名称':'company_name', '省份':'province', '城市':'city', '主营业务收入(201712)':'main_bussiness_income', '净利润(201712)':'net_profit', '员工人数':'employees', '上市日期':'listing_date', '招股书':'zhaogushu', '公司财报':'financial_report', '行业分类':'industry_classification', '产品类型':'industry_type', '主营业务':'main_business'},inplace = True)

    # print(tbl)
    return tbl
    # rename将中文名改为英文名,便于存储到mysql及后期进行数据分析
    # tbl = pd.DataFrame(tbl,dtype = 'object') #dtype可统一修改列格式为文本

def generate_mysql():
    conn = pymysql.connect(
        host='localhost',
        user='root',
        password='******',
        port=3306,
        charset = 'utf8',  
        db = 'wade')
    cursor = conn.cursor()

    sql = 'CREATE TABLE IF NOT EXISTS listed_company (serial_number INT(20) NOT NULL,stock_code INT(20) ,stock_abbre VARCHAR(20) ,company_name VARCHAR(20) ,province VARCHAR(20) ,city VARCHAR(20) ,main_bussiness_income VARCHAR(20) ,net_profit VARCHAR(20) ,employees INT(20) ,listing_date DATETIME(0) ,zhaogushu VARCHAR(20) ,financial_report VARCHAR(20) , industry_classification VARCHAR(20) ,industry_type VARCHAR(100) ,main_business VARCHAR(200) ,PRIMARY KEY (serial_number))'
    # listed_company是要在wade数据库中建立的表,用于存放数据

    cursor.execute(sql)
    conn.close()


def write_to_sql(tbl, db = 'wade'):
    engine = create_engine('mysql+pymysql://root:******@localhost:3306/{0}?charset=utf8'.format(db))
    try:
        # df = pd.read_csv(df)
        tbl.to_sql('listed_company2',con = engine,if_exists='append',index=False)
        # append表示在原有表基础上增加,但该表要有表头
    except Exception as e:
        print(e)


def main(page):
    generate_mysql()
    for i in range(1,page):  
        html = get_one_page(i)
        tbl = parse_one_page(html)
        write_to_sql(tbl)

# # 单进程
if __name__ == '__main__':    
    main(178)

    endtime = time.time()-start_time
    print('程序运行了%.2f秒' %endtime)


# 多进程
# from multiprocessing import Pool
# if __name__ == '__main__':
#     pool = Pool(4)
#     pool.map(main, [i for i in range(1,178)])  #共有178页

#     endtime = time.time()-start_time
#     print('程序运行了%.2f秒' %(time.time()-start_time))
  • 声明
    这个抓取程序是参考了第2大脑公众号文章,本人新手,其中很多基础东西都不了解所以解体详述了本文中会用到的所有基本概念,通过细读本文涉及内容,0基础可以完全搞懂爬虫。

猜你喜欢

转载自blog.csdn.net/The_Time_Runner/article/details/83855723