前言
前段时间出于任务需要,需要爬取上证50指数成分股在某些日期内的新闻数量。
一开始的想法是爬百度新闻高级搜索,但是后来某一天百度新闻高级搜索突然用不了,无论搜索什么都会跳转到百度主页,至今(2020.06.11)没有恢复,不知道是不是百度公司把这个业务停掉了。
于是只能找替代品了,博主盯上了中国新闻高级搜索,号称是搜索界的国家队。
分析
网页解析
欲爬虫,首先得解析网页url结构。
首先以全文搜索关键词“工商银行”为例,设置搜索起点2020-01-08,终点2020-01-08,即搜索2020年1月8日这一天关键词“工商银行”的新闻量,点此进入网页。
主页如下:
其中,新闻总量便是我们期望爬取的数据,而搜索关键词和搜索时间区间是我们在爬虫过程中需要改变的变量。
接着分析其url:http://news.chinaso.com/newssearch.htm?q=%E5%B7%A5%E5%95%86%E9%93%B6%E8%A1%8C&startTime=20200108&endTime=20200108
在这一串url中,前面的http://news.chinaso.com/newssearch.htm?部分代表的是国搜主页,爬虫过程中不需要改动;余下部分则由三个变量构成:q、startTime、endTime。变量q的含义为搜索关键词,在这个url中“%E5%B7%A5%E5%95%86%E9%93%B6%E8%A1%8C”即代表着关键词“工商银行”;变量startTime和endTime即代表着时间起点和终点。
在实际操作过程中,url编码太过繁琐复杂,可直接用中文文本代替,例如,我想搜索2020年1月8日这一天关键词“中国平安”的新闻量,可以如下方式构造url:http://news.chinaso.com/newssearch.htm?q=中国平安&startTime=20200108&endTime=20200108。
对于希望爬取到的数据新闻总量,首先利用F12开发人员工具对其进行定位:
找到其节点后便可解析新闻量,方法有很多,包括bs、正则等,在这里博主使用正则语句:
num=re.match('<div class="toolTab_xgxwts">为您找到相关新闻(.*)篇</div>',str(retext))
到这里,爬虫最基本的部分就完成了,余下一些头尾部分细节需要处理。
股票数据来源
一开始就说了需要爬取上证50成分股的股票,那首先需要知道成分股有哪些吧。博主了解到一支指数的成分股不是固定的,由于需要爬取的时间周期较长(一年以上),比较稳妥的做法是对于爬取的每个日期都请求一次成分股名单再进行爬取。在这里博主使用了聚宽平台提供的jqdatasdk数据包。首次使用需在官网注册账号并且在命令行中输入pip install jqdatasdk
安装。
在使用过程中,首先需要登录:
jqdatasdk.auth('xxxxxx','xxxxxx')#使用你的账号密码登陆joinquant
接着便可获取历史行情数据:
raw_data_everyday = jqdatasdk.get_index_weights('000016.XSHG', date=temporaryTime1_raw)
从中便可解析出每日的成分股列表,用于循环爬取。
代理IP
在实际操作中发现国搜对于爬虫还是比较严格的(不愧是国家队),需要使用代理ip,具体的ip平台这里不赘述,避免被当作广告,只提一下使用代理ip的方法。
首先构建ip池,从ip提供方复制下可用ip及其端口(当然也可接入其接口)
proxies_list=['58.218.200.227:8601', '58.218.200.223:3841', '58.218.200.226:3173', '58.218.200.228:8895', '58.218.200.226:8780', '58.218.200.227:6646', '58.218.200.228:7469', '58.218.200.228:5760', '58.218.200.223:8830', '58.218.200.228:5418', '58.218.200.223:6918', '58.218.200.225:5211', '58.218.200.227:8141', '58.218.200.228:7779', '58.218.200.226:3999', '58.218.200.226:3345', '58.218.200.228:2433', '58.218.200.226:6042', '58.218.200.225:4760', '58.218.200.228:2547', '58.218.200.225:3886', '58.218.200.226:7384', '58.218.200.228:8604', '58.218.200.227:6996', '58.218.200.223:3986', '58.218.200.226:6305', '58.218.200.225:6208', '58.218.200.223:4006', '58.218.200.225:8079', '58.218.200.228:7042', '58.218.200.225:7086', '58.218.200.227:8913', '58.218.200.227:3220', '58.218.200.226:2286', '58.218.200.228:7337', '58.218.200.227:2010', '58.218.200.227:9062', '58.218.200.225:8799', '58.218.200.223:3568', '58.218.200.228:3184', '58.218.200.223:5874', '58.218.200.225:3963', '58.218.200.228:3696', '58.218.200.227:7113', '58.218.200.226:4501', '58.218.200.223:7636', '58.218.200.225:9108', '58.218.200.228:6940', '58.218.200.223:5310', '58.218.200.225:2864', '58.218.200.226:5225', '58.218.200.228:6468', '58.218.200.223:8127', '58.218.200.225:8575', '58.218.200.223:7269', '58.218.200.228:7039', '58.218.200.226:6674', '58.218.200.226:5945', '58.218.200.225:3108', '58.218.200.226:3990', '58.218.200.223:8356', '58.218.200.227:5274', '58.218.200.227:6535', '58.218.200.225:3934', '58.218.200.223:6866', '58.218.200.227:3088', '58.218.200.227:7253', '58.218.200.223:2215', '58.218.200.228:2715', '58.218.200.226:4071', '58.218.200.228:7232', '58.218.200.225:5561', '58.218.200.226:7476', '58.218.200.223:3917', '58.218.200.227:2931', '58.218.200.223:5612', '58.218.200.226:6409', '58.218.200.223:7785', '58.218.200.228:7906', '58.218.200.227:8476', '58.218.200.227:3012', '58.218.200.226:6388', '58.218.200.225:8819', '58.218.200.225:2093', '58.218.200.227:4408', '58.218.200.225:7457', '58.218.200.223:3593', '58.218.200.225:2028', '58.218.200.227:2119', '58.218.200.223:3094', '58.218.200.226:3232', '58.218.200.227:6769', '58.218.200.223:4013', '58.218.200.227:9064', '58.218.200.223:6034', '58.218.200.227:4292', '58.218.200.228:5228', '58.218.200.228:2397', '58.218.200.226:2491', '58.218.200.226:3948', '58.218.200.227:2630', '58.218.200.228:4857', '58.218.200.228:2541', '58.218.200.225:5653', '58.218.200.226:7068', '58.218.200.223:2129', '58.218.200.227:4093', '58.218.200.226:2466', '58.218.200.226:4089', '58.218.200.225:4932', '58.218.200.228:8511', '58.218.200.227:6660', '58.218.200.227:2536', '58.218.200.226:5777', '58.218.200.228:4755', '58.218.200.227:4138', '58.218.200.223:5297', '58.218.200.226:2367', '58.218.200.225:7920', '58.218.200.225:6752', '58.218.200.228:4508', '58.218.200.223:3120', '58.218.200.227:3329', '58.218.200.226:6911', '58.218.200.228:7032', '58.218.200.223:8029', '58.218.200.228:2009', '58.218.200.223:3487', '58.218.200.228:9078', '58.218.200.225:3985', '58.218.200.227:6955', '58.218.200.228:8847', '58.218.200.228:4376', '58.218.200.225:3942', '58.218.200.228:4983', '58.218.200.225:9082', '58.218.200.225:7907', '58.218.200.226:6141', '58.218.200.226:5268', '58.218.200.226:4986', '58.218.200.223:8374', '58.218.200.226:4850', '58.218.200.225:5397', '58.218.200.226:2983', '58.218.200.225:3156', '58.218.200.226:6176', '58.218.200.225:4273', '58.218.200.226:8625', '58.218.200.226:8424', '58.218.200.226:5714', '58.218.200.223:8166', '58.218.200.226:4194', '58.218.200.223:6850', '58.218.200.228:6994', '58.218.200.223:3825', '58.218.200.226:7129', '58.218.200.223:3941', '58.218.200.227:8775', '58.218.200.228:4195', '58.218.200.227:4570', '58.218.200.223:3255', '58.218.200.225:6626', '58.218.200.226:8286', '58.218.200.225:4605', '58.218.200.223:3667', '58.218.200.223:7281', '58.218.200.225:6862', '58.218.200.228:2340', '58.218.200.227:7144', '58.218.200.223:3691', '58.218.200.228:3849', '58.218.200.228:7871', '58.218.200.225:6678', '58.218.200.225:6435', '58.218.200.223:3726', '58.218.200.226:8436', '58.218.200.223:7461', '58.218.200.223:4113', '58.218.200.223:3912', '58.218.200.225:4666', '58.218.200.227:7176', '58.218.200.225:5462', '58.218.200.225:8643', '58.218.200.227:7591', '58.218.200.227:2134', '58.218.200.227:5480', '58.218.200.228:9013', '58.218.200.227:5178', '58.218.200.223:8970', '58.218.200.223:5423', '58.218.200.227:2832', '58.218.200.225:5636', '58.218.200.223:2347', '58.218.200.227:4171', '58.218.200.227:5288', '58.218.200.227:4254', '58.218.200.227:3254', '58.218.200.228:6789', '58.218.200.223:4956', '58.218.200.226:6146']
对每次爬虫,从ip池中随机挑选一个ip用于伪装。
def randomip_scrapy(proxies_list,url,headers):
proxy_ip = random.choice(proxies_list)
proxies = {'https': 'https://'+proxy_ip,'http':'http://'+proxy_ip}
R=requests.get(url,headers=headers,proxies=proxies)
return R
代码实现
所有的技术难点都分析完了,接下来直接放上完整代码,博主应用requests
模块请求html:
#遍历股票 遍历日期 输出某股每日数据1*n矩阵 把m支股票数据整合成m*n矩阵
import requests
from bs4 import BeautifulSoup
import re
import datetime
import random
from retrying import retry
import jqdatasdk
import pandas as pd
import numpy as np
jqdatasdk.auth('xxxxxx','xxxxxx')#使用你的账号登陆joinquant
@retry(stop_max_attempt_number=100)#重试
def randomip_scrapy(proxies_list,url,headers):
proxy_ip = random.choice(proxies_list)
proxies = {'https': 'https://'+proxy_ip,'http':'http://'+proxy_ip}
R=requests.get(url,headers=headers,proxies=proxies)
return R
#需要做到遍历每一股、每一天的数据
def newsCrawls(start_year,start_month,start_day,proxies_list,time_step,time_count):
startTime_raw = datetime.date(year=start_year,month=start_month,day=start_day)
temporaryTime1_raw = startTime_raw#定义临时日期
temporaryTime2_raw = startTime_raw+datetime.timedelta(days=time_step)
#结构化日期
#startTime = startTime_raw.strftime('%Y%m%d')
j = 0
#时间序列内遍历
while j < time_count*time_step:
#对于每一天,创建三个需要记录数据的存储列表
name_list_everyday = []
weight_list_everyday = []
newsnum_list_everyday = []
date_list = []
temporaryTime2_raw = temporaryTime1_raw+datetime.timedelta(days=time_step)
temporaryTime1 = temporaryTime1_raw.strftime('%Y%m%d')#结构化临时日期1
temporaryTime2 = temporaryTime2_raw.strftime('%Y%m%d')#结构化临时日期2
#从jqdata获取数据并解析
raw_data_everyday = jqdatasdk.get_index_weights('000016.XSHG', date=temporaryTime1_raw)#如果要改指数在这里改
raw_data_everyday_array = np.array(raw_data_everyday)
raw_data_everyday_list = raw_data_everyday_array.tolist()
for list in raw_data_everyday_list:
name_list_everyday.append(list[1])
weight_list_everyday.append(list[0])
date_list.append(list[2])
j = j + 1
count = 0
for name in name_list_everyday:
url="http://news.chinaso.com/newssearch.htm?q="+str(name)+"&type=news&page=0&startTime="+str(temporaryTime1)+"&endTime="+str(temporaryTime2)
user_agent_list = ["Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15",
]
#cookies="uid=CgqAiV1r244oky3nD6KkAg==; wdcid=5daa44151f0a6fc9; cookie_name=222.16.63.188.1567349674276674; Hm_lvt_91fa1aefc990a9fc21c08506e5983ddf=1567349649,1567349703; Hm_lpvt_91fa1aefc990a9fc21c08506e5983ddf=1567349703; wdlast=1567351002"
agent = random.choice(user_agent_list)#随机agent
headers = {
'User-Agent':agent
}
#如选取不可用ip回到这一步
r = randomip_scrapy(proxies_list,url,headers)
#proxy_ip = random.choice(proxies_list)
#proxies = {'https': 'https://'+proxy_ip,'http':'http://'+proxy_ip}
#r=requests.get(url,headers=headers,proxies=proxies)
content = r.content
soup=BeautifulSoup(content,'lxml')
retext=soup.find(class_='toolTab_xgxwts')
num=re.match('<div class="toolTab_xgxwts">为您找到相关新闻(.*?)篇</div>',str(retext))
count = count + 1
print('第',count,'个')
if str(num)=='None':#如果没爬到说明该股当天无新闻,输入0
newsnum_list_everyday.append(0)
else:
newsnum_list_everyday.append(int(num.group(1)))
#下一步就是把每天的四列表存入csv数据库
a = [num for num in newsnum_list_everyday]
b = [name for name in name_list_everyday]
c = [weight for weight in weight_list_everyday]
d = [date for date in date_list]
dataframe = pd.DataFrame({'num':a,'name':b,'weight':c,'date':d})
dataframe.to_csv(r"20181022-20181231.csv",mode = 'a',sep=',',encoding="gb2312")
temporaryTime1_raw = temporaryTime1_raw+datetime.timedelta(days=time_step)#日期+步长
if __name__ == "__main__":
proxies_list=['58.218.200.227:8601', '58.218.200.223:3841', '58.218.200.226:3173', '58.218.200.228:8895', '58.218.200.226:8780', '58.218.200.227:6646', '58.218.200.228:7469', '58.218.200.228:5760', '58.218.200.223:8830', '58.218.200.228:5418', '58.218.200.223:6918', '58.218.200.225:5211', '58.218.200.227:8141', '58.218.200.228:7779', '58.218.200.226:3999', '58.218.200.226:3345', '58.218.200.228:2433', '58.218.200.226:6042', '58.218.200.225:4760', '58.218.200.228:2547', '58.218.200.225:3886', '58.218.200.226:7384', '58.218.200.228:8604', '58.218.200.227:6996', '58.218.200.223:3986', '58.218.200.226:6305', '58.218.200.225:6208', '58.218.200.223:4006', '58.218.200.225:8079', '58.218.200.228:7042', '58.218.200.225:7086', '58.218.200.227:8913', '58.218.200.227:3220', '58.218.200.226:2286', '58.218.200.228:7337', '58.218.200.227:2010', '58.218.200.227:9062', '58.218.200.225:8799', '58.218.200.223:3568', '58.218.200.228:3184', '58.218.200.223:5874', '58.218.200.225:3963', '58.218.200.228:3696', '58.218.200.227:7113', '58.218.200.226:4501', '58.218.200.223:7636', '58.218.200.225:9108', '58.218.200.228:6940', '58.218.200.223:5310', '58.218.200.225:2864', '58.218.200.226:5225', '58.218.200.228:6468', '58.218.200.223:8127', '58.218.200.225:8575', '58.218.200.223:7269', '58.218.200.228:7039', '58.218.200.226:6674', '58.218.200.226:5945', '58.218.200.225:3108', '58.218.200.226:3990', '58.218.200.223:8356', '58.218.200.227:5274', '58.218.200.227:6535', '58.218.200.225:3934', '58.218.200.223:6866', '58.218.200.227:3088', '58.218.200.227:7253', '58.218.200.223:2215', '58.218.200.228:2715', '58.218.200.226:4071', '58.218.200.228:7232', '58.218.200.225:5561', '58.218.200.226:7476', '58.218.200.223:3917', '58.218.200.227:2931', '58.218.200.223:5612', '58.218.200.226:6409', '58.218.200.223:7785', '58.218.200.228:7906', '58.218.200.227:8476', '58.218.200.227:3012', '58.218.200.226:6388', '58.218.200.225:8819', '58.218.200.225:2093', '58.218.200.227:4408', '58.218.200.225:7457', '58.218.200.223:3593', '58.218.200.225:2028', '58.218.200.227:2119', '58.218.200.223:3094', '58.218.200.226:3232', '58.218.200.227:6769', '58.218.200.223:4013', '58.218.200.227:9064', '58.218.200.223:6034', '58.218.200.227:4292', '58.218.200.228:5228', '58.218.200.228:2397', '58.218.200.226:2491', '58.218.200.226:3948', '58.218.200.227:2630', '58.218.200.228:4857', '58.218.200.228:2541', '58.218.200.225:5653', '58.218.200.226:7068', '58.218.200.223:2129', '58.218.200.227:4093', '58.218.200.226:2466', '58.218.200.226:4089', '58.218.200.225:4932', '58.218.200.228:8511', '58.218.200.227:6660', '58.218.200.227:2536', '58.218.200.226:5777', '58.218.200.228:4755', '58.218.200.227:4138', '58.218.200.223:5297', '58.218.200.226:2367', '58.218.200.225:7920', '58.218.200.225:6752', '58.218.200.228:4508', '58.218.200.223:3120', '58.218.200.227:3329', '58.218.200.226:6911', '58.218.200.228:7032', '58.218.200.223:8029', '58.218.200.228:2009', '58.218.200.223:3487', '58.218.200.228:9078', '58.218.200.225:3985', '58.218.200.227:6955', '58.218.200.228:8847', '58.218.200.228:4376', '58.218.200.225:3942', '58.218.200.228:4983', '58.218.200.225:9082', '58.218.200.225:7907', '58.218.200.226:6141', '58.218.200.226:5268', '58.218.200.226:4986', '58.218.200.223:8374', '58.218.200.226:4850', '58.218.200.225:5397', '58.218.200.226:2983', '58.218.200.225:3156', '58.218.200.226:6176', '58.218.200.225:4273', '58.218.200.226:8625', '58.218.200.226:8424', '58.218.200.226:5714', '58.218.200.223:8166', '58.218.200.226:4194', '58.218.200.223:6850', '58.218.200.228:6994', '58.218.200.223:3825', '58.218.200.226:7129', '58.218.200.223:3941', '58.218.200.227:8775', '58.218.200.228:4195', '58.218.200.227:4570', '58.218.200.223:3255', '58.218.200.225:6626', '58.218.200.226:8286', '58.218.200.225:4605', '58.218.200.223:3667', '58.218.200.223:7281', '58.218.200.225:6862', '58.218.200.228:2340', '58.218.200.227:7144', '58.218.200.223:3691', '58.218.200.228:3849', '58.218.200.228:7871', '58.218.200.225:6678', '58.218.200.225:6435', '58.218.200.223:3726', '58.218.200.226:8436', '58.218.200.223:7461', '58.218.200.223:4113', '58.218.200.223:3912', '58.218.200.225:4666', '58.218.200.227:7176', '58.218.200.225:5462', '58.218.200.225:8643', '58.218.200.227:7591', '58.218.200.227:2134', '58.218.200.227:5480', '58.218.200.228:9013', '58.218.200.227:5178', '58.218.200.223:8970', '58.218.200.223:5423', '58.218.200.227:2832', '58.218.200.225:5636', '58.218.200.223:2347', '58.218.200.227:4171', '58.218.200.227:5288', '58.218.200.227:4254', '58.218.200.227:3254', '58.218.200.228:6789', '58.218.200.223:4956', '58.218.200.226:6146']
newsCrawls(2018,12,17,proxies_list,1,15)#年、月、日、时间步长、多少个时间步长
#22.46
总结
这次项目规模不算小,但是要解决的技术难点均为爬虫的基本问题,所以整体难度不算太高。在这次项目中博主算是首次完整地应用了所学的爬虫知识,包括数据获取,网页解析,ip欺骗,数据清洗等。也算是对自己的一次锻炼。
原文链接:https://blog.csdn.net/chandler_scut/article/details/106685617