多线程爬取网络数据的简单实现
Posted 止一之路
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程爬取网络数据的简单实现相关的知识,希望对你有一定的参考价值。
最近研究了一下网络爬虫,原来和自己以前经常做的从网上抓去数据,原理其实是差不多的。
大概就是通过代码来模拟浏览器,打开网页、拷贝数据。
我之前在做港股打新收益统计的时候,一直都是手工进行的,虽然数据量不大,但也还是稍显笨拙,这回就通过抓取港股新股为例,说说如何抓取网络数据,然后下载到数据库的。
首先是网络数据抓取,这里主要用到的包是requests,所以第一步就是:
import requests
由于要模拟浏览器,所以浏览器的请求头也是要的,这些信息可以通过F12开发者工具看到:
所以此处要有请求头:
# 得到请求头def get_headers(url):
headers = { 'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6', 'Connection': 'keep-alive', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'Upgrade-Insecure-Requests': '1', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) '
'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36',
}
host = url.split('//')[1].split('/')[0]
headers['Host'] = host
return headers
腾讯的新股数据是这样的:
我之前都是通过手工一页一页复制,现在想起来都觉得蠢。
这些数据可以直接通过这个链接得到:
http://ifzq.gtimg.cn/appstock/app/HkNewStock/getNewPreview?app=web
你问我怎么知道的,还是F12开发者工具:
想要找到这个,你得花点时间:
因为是根据一手中签率统计收益情况的,在腾讯新股数据这边,没有一手股数的数据,怎么办呢?
我找到了新浪的新股数据:
http://vip.stock.finance.sina.com.cn/q/view/hk_IPOList.php
点开链接,在详情页里面就有啦:
怎么从页面找到定位到这个数据呢?这里需要用到lxml包:
from lxml import html
通过xpath可以直接选中对应的节点:
首先是根节点:
# 得到xpath的root节点def get_xpath_root(url):
headers = get_headers(url)
resp = requests.get(url, headers=headers, verify = False)
page = resp.content
root = html.fromstring(page)
return root
然后是子节点,例如这里:
就可以通过xpath //*[@class="content"]//th
和 //*[@class="content"]//td
选择到所有的 th 和 td ,然后就有了如下方法:
# 从新浪数据中获取IPO的详情数据def get_XL_IPO_details(url):
root = get_xpath_root(url)
elementsTh = root.xpath('//*[@class="content"]//th')
elementsTd = root.xpath('//*[@class="content"]//td')
keys = list(map(lambda x:x.text,elementsTh))
keys = ['code', 'name'] + keys[2:] #values = list(map(lambda x:x.text,elementsTd))
values = list(map(lambda x: x.text if len(x.xpath('./p')) == 0 else x.xpath('./p')[0].text.strip(),elementsTd))
se = pd.Series(index = keys,data = values,name = values[0]) return se.to_frame().T
因为每次都需要打开股票的详情页,然后抓取里面的每手股数数据,这样需要的时间就比较多。
为了解决这个问题,可以通过多线程的方式同时进行,涉及到的包是multiprocessing
。下面先是通过多线程并行处理获取数据,放到一个列表中,然后再组合到一起。
# 多线程获取数据pool = ThreadPool(processes=16)
frame_list = pool.map(get_XL_IPO_details, url_list)
pool.close()
pool.join()
df_xl = pd.concat(frame_list, axis=0)
df_xl = df_xl.reset_index(drop=True)
这样一来,相对于直接遍历,速度就快多了。
关于xpath的语法,可以参考链接:
http://www.w3school.com.cn/xpath/xpath_syntax.asp
因为策略的逻辑是:
1.筛选条件:总募资额小于等于1亿;
2.上市首日按照实际招股价上浮10%委托卖出,如果委托卖出不成功,则收盘前按照市价卖出。
除了新股的信息数据之外,还需要有新股上市首日的行情数据。找到数据的方式和前面类似:
所以就有了:
# 从腾讯获取开收高价格数据def get_TX_IPO_price(item):
code = item[0]
date = item[1][:10]
url = 'http://web.ifzq.gtimg.cn/appstock/app/hkfqkline/get?_var=¶m=hk{0},day,,,3200,qfq'.format(code)
headers = get_headers(url)
resp = requests.get(url, headers=headers, verify=False)
content = resp.text
result_dict = eval(content.replace('null', 'NAN'))
result_dict = result_dict['data']['hk' + code] if 'qfqday' in result_dict.keys():
result_list = result_dict['qfqday'] else:
result_list = result_dict['day'] if len(result_list) <= 0:
se = pd.Series(index =['code','date','open','close','high'],data = [code] + [np.NaN] * 4) return se.to_frame().T
df = pd.DataFrame(result_list)
df.index = df.iloc[:, 0]
df = df[df.index >= date] if len(df) > 0:
date = df.iloc[0][0]
openPrice = df.iloc[0][1]
closePrice = df.iloc[0][2]
highPrice = df.iloc[0][3]
se = pd.Series(index =['code','date','open','close','high'],data = [code, date, openPrice, closePrice, highPrice]) else:
se = pd.Series(index =['code','date','open','close','high'],data = [code] + [np.NaN] * 4)
return se.to_frame().T
然后还是多线程同时并行:
# 多线程获取数据pool = ThreadPool(processes=16)
frame_list = pool.map(get_TX_IPO_price, item_list)
pool.close()
pool.join()
df_price = pd.concat(frame_list, axis=0)
df_price = df_price.reset_index(drop=True)
数据获取完了之后,就是保存到数据库,这里需要用到包sqlalchemy
:
from sqlalchemy import create_engine
然后就是保存到数据库的方法:
# 保存到数据库def save_to_database(connect, table, data):
# 先读取数据
try:
data_in_database = pd.read_sql(table, connect,index_col='index')
code_list = data_in_database.code.tolist() except:
code_list = [] # 过滤数据库中已经存在的数据
code_list_to_save = list(filter(lambda x:x not in code_list,data.code.tolist()))
data = data[data.code.isin(code_list_to_save)] # 然后写入数据库中没有的数据
data.to_sql(table, connect, schema='finance', if_exists='append')
执行保存操作:
connect = create_engine('mysql://root:123456@localhost:3306/finance?charset=utf8')
save_to_database(connect, 'hk_ipo_info_tx_tbl',df_tx)
save_to_database(connect, 'hk_ipo_info_xl_tbl',df_xl)
save_to_database(connect, 'hk_ipo_price_info_tbl',df_price)
读取也很简单,这样就行了:
connect = create_engine('mysql://root:123456@localhost:3306/finance?charset=utf8')# 读取腾讯数据df_tx = pd.read_sql('hk_ipo_info_tx_tbl', connect,index_col='index')# 读取新浪数据df_xl = pd.read_sql('hk_ipo_info_xl_tbl', connect,index_col='index')# 读取价格数据df_price = pd.read_sql('hk_ipo_price_info_tbl', connect,index_col='index')
就这样,从网络上抓取数据,然后保存到数据库的工作就完成了。
最终我们的目的,港股打新最近一年期望收益情况的统计的结果是这样的:
详情打印如下:
无数据库部分代码可以点击阅读原文获取。
iOS 用户快速赞赏通道
长按二维码赞赏
止一之路
和您一起走上财务自由的正道
以上是关于多线程爬取网络数据的简单实现的主要内容,如果未能解决你的问题,请参考以下文章