背景
爬虫技术是索取互联网信息主要手段,一直想接触,但并没有实体项目。近期偶尔关注股票,一位朋友非常强调每个公司的公告信息的重要性,甚至在短期内极度影响一直股票的走势。
所以,自己基于scrapy+python实现了一个小爬虫,目标是爬取股票公告,并入库,以待以后的机器学习分析。
概要设计
功能描述
- 爬虫服务爬取目标网站数据(包括翻页之后的数据)
- 将数据进行入库操作
模块设计
详细设计
目标网站分析
地址:http://data.eastmoney.com/notices/stock/002415.html
这个地址的尾号是股票代码002415,那么我们在url里面不加代码,直接访问会是怎么样呢?
地址:http://data.eastmoney.com/notices/
点击一下下一页,看看network里面返回了什么内容
复制地址如下:
http://data.eastmoney.com/notices/getdata.ashx?StockCode=&FirstNodeType=0&CodeType=1&PageIndex=2&PageSize=50&jsObj=CuxhmQJG&SecNodeType=0&Time=&rt=52105407
- PageIndex应该就是分页的页数
- PageSize,是每页的数量
我们用浏览器访问一下看看结果:
是一段js的代码,不过后面的数据全部是json的,这些数据基本可以被我们所用。
把数据格式化一下看看,是什么样的。
最下面这个url,应该就是详情页,进去看看
那么,我们的目标就初步达成了:
- 获取股票目录列表,并且还可以分页
- 获取股票公告详情页
数据库表设计
字段分析
勾选出来的字段依次是:
- 标题
- 股票代码
- 股票名称
- 股票交易所类型
- 股票公告详情地址
库表设计语句
create database if not exists dbstock;
CREATE TABLE `t_stock` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`code` bigint(20) NOT NULL,
`name` varchar(255) NOT NULL,
`title` varchar(255) DEFAULT NULL,
`publish_time` varchar(255) DEFAULT NULL,
`text` longtext,
`text_md5_hex` varchar(100) NOT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `text_md5_hex_uniq` (`text_md5_hex`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3550 DEFAULT CHARSET=utf8mb4
项目实战
安装scrapy(默认已经安装了python)
pip install scrapy
创建项目
打开anaconda prompt,进入到一个空目录(我们希望创建项目的目录),输入:
scrapy startproject crawler
命令来创建项目(crawler是项目名称,可任意)
创建爬虫
cd crawler
scrapy genspider announcement example.com 创建爬虫
pycharm打开项目
打开pycharm,file->open,输入F:\my_code\crawler,打开爬虫项目
运行hello_world
修改start_urls
为了启动方便,添加一个启动入口(也就是main函数之类的)
# -*- coding: utf-8 -*-
from scrapy.utils.project import get_project_settings
from scrapy.crawler import CrawlerProcess
process = CrawlerProcess(get_project_settings())
process.crawl('announcement')
process.start()
放到保存后放到根目录即可
运行看一下结果:
解析字段
获取翻页字段
注:
text = response.xpath('//div[@class="detail-body"]/div/text()').extract_first()
使用了xpath的语法,这里需要对xpath有基础的了解
添加items,为入库做准备(也就是java中的model)
注意,StockItem中的每个字段必须和数据库的字段名保持一致
添加pipelines,进行入库操作(也就是java中的dao)
# -*- coding: utf-8 -*-
import utils.db_connection_pool as pool
from crawler.items import StockItem
import config.setting as setting
# 指定表格
table_dic = {
StockItem: 'dbstock.t_stock',
}
# 指定数据库参数
PYMYSQL_ONE = dict(
host=setting.DB_HOST,
port=setting.DB_PORT,
user=setting.DB_USER,
password=setting.DB_PASSWORD,
)
class CrawlerPipeline(object):
def __init__(self):
# init中获取数据库连接
self.conn = pool.get_connection(**PYMYSQL_ONE)
# 拼接sql参数
def get_params(self, columns):
params_format = ['%s'] * len(columns)
return ','.join(params_format)
# 回调执行数据库操
def process_item(self, item, spider):
sql = "REPLACE INTO {} ({}) VALUES ({})".format(
table_dic[type(item)],
','.join(item.keys()),
self.get_params(item.keys())
)
cur = self.conn.cursor()
cur.execute(sql, list(getattr(item, '_values').values()))
cur.close()
return item
修改配置,启动这个pipeline
完成入库
总结
此项目做了一个最小化的爬虫实现,仅供入门参考
后续补充
utils/db_connection_pool.py
# -*- coding: UTF-8 -*-
"""
@描述:数据库连接池管理模块
"""
import pymysql
from DBUtils.PooledDB import PooledDB
import config.setting as setting
pool_dict = dict()
def get_connection(host, port, user, password):
if host in pool_dict:
return pool_dict.get(host).get_conn()
else:
pool = ConnectionPool(host, port, user, password)
pool_dict.setdefault(host, pool)
return pool.get_conn()
class ConnectionPool(object):
__pool = None
def __init__(self, host, port, user, password):
if self.__pool is None:
self.__pool = PooledDB(creator=pymysql, mincached=setting.DB_MIN_CACHED, maxcached=setting.DB_MAX_CACHED,
maxshared=setting.DB_MAX_SHARED, maxconnections=setting.DB_MAX_CONNECYIONS,
blocking=setting.DB_BLOCKING, maxusage=setting.DB_MAX_USAGE,
setsession=setting.DB_SET_SESSION,
host=host, port=port,
user=user, passwd=password, use_unicode=False,
charset=setting.DB_CHARSET)
def get_conn(self):
return self.__pool.connection()
config/setting_dev.py
# coding:utf-8
'''
@描述:数据库配置信息
'''
# 大数据数据库
DB_HOST = "172.16.200.17"
DB_PORT = 3306
DB_USER = "xxx"
DB_PASSWORD = "xxxx"
# 数据库连接编码
DB_CHARSET = "utf8"
# mincached : 启动时开启的闲置连接数量(缺省值 0 以为着开始时不创建连接)
DB_MIN_CACHED = 10
# maxcached : 连接池中允许的闲置的最多连接数量(缺省值 0 代表不闲置连接池大小)
DB_MAX_CACHED = 10
# maxshared : 共享连接数允许的最大数量(缺省值 0 代表所有连接都是专用的)如果达到了最大数量,被请求为共享的连接将会被共享使用
DB_MAX_SHARED = 20
# maxconnecyions : 创建连接池的最大数量(缺省值 0 代表不限制)
DB_MAX_CONNECYIONS = 20
# blocking : 设置在连接池达到最大数量时的行为(缺省值 0 或 False 代表返回一个错误<toMany......>; 其他代表阻塞直到连接数减少,连接被分配)
DB_BLOCKING = True
# maxusage : 单个连接的最大允许复用次数(缺省值 0 或 False 代表不限制的复用).当达到最大数时,连接会自动重新连接(关闭和重新打开)
DB_MAX_USAGE = 0
# setsession : 一个可选的SQL命令列表用于准备每个会话,如["set datestyle to german", ...]
DB_SET_SESSION = ['SET AUTOCOMMIT = 1']