多线程爬取网络数据的简单实现

Posted 止一之路

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程爬取网络数据的简单实现相关的知识,希望对你有一定的参考价值。

 
  阅读时间 
  8 分钟 
 

资讯   |   干货   |   思考   |   杂谈

最近研究了一下网络爬虫,原来和自己以前经常做的从网上抓去数据,原理其实是差不多的。

大概就是通过代码来模拟浏览器,打开网页、拷贝数据。

我之前在做港股打新收益统计的时候,一直都是手工进行的,虽然数据量不大,但也还是稍显笨拙,这回就通过抓取港股新股为例,说说如何抓取网络数据,然后下载到数据库的。

首先是网络数据抓取,这里主要用到的包是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=&param=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 用户快速赞赏通道

长按二维码赞赏



多线程爬取网络数据的简单实现

多线程爬取网络数据的简单实现

多线程爬取网络数据的简单实现

多线程爬取网络数据的简单实现

多线程爬取网络数据的简单实现

多线程爬取网络数据的简单实现



多线程爬取网络数据的简单实现微信ID:zhiyizhilu
长按左侧二维码关注

止一之路

和您一起走上财务自由的正道

 
   戳原文,拷贝代码。



以上是关于多线程爬取网络数据的简单实现的主要内容,如果未能解决你的问题,请参考以下文章

Python多线程爬虫爬取电影天堂资源

实现多线程爬取数据并保存到mongodb

Python实现简单多线程任务队列

基于 PHP 的数据爬取(QueryList)

Python网络爬虫四多线程爬取多张百度图片的图片

Python 爬虫多线程爬取