【项目记录】雪球网股票组合数据爬虫(包括雪球模拟登录代码)

室友上半年跟了一个做机器学习方向的导师做股票投资组合的项目,暑假来找我帮忙弄点股票组合的数据来测试算法。目前国内股票资讯网站大约只有雪球能够比较方便地获取大规模的股票组合数据,七月疲于双专生活不能自理,八月断断续续地弄了一阵子,碰了一鼻子灰总算有些摸透了雪球网的套路。这里分享一下我在爬取雪球网数据时遇到的问题,一方面是对自己项目的一个小结,另一方面给其他需要爬取雪球网数据的小伙伴们提供一些参考,也是少走点弯路。

准备工具

Python 3.6(Python 2.x版本的可能在调用库上存在差异)

ADB(可选,主要用于处理短信验证码的自动化获取)

 

项目目的

获取雪球网优质组合的净值及调仓记录的历史数据

 

解决思路

事实上雪球网大部分的数据都可以不必登录就可以查看并且通过爬虫获取(如用户个人信息,股票数据,资讯文章,股票组合的历史净值数据等),不需要登录验证就可以获取的数据爬取起来相对容易,简单通过浏览器F12监听抓包获取请求信息的参数值,然后就可以编写爬虫批量获取了。但是对于本次项目最为关键的数据是组合(尤其是优质组合)的调仓历史数据则是必须通过登录才能获取。由于其他数据的获取方式与登录后获取调仓记录的历史数据的方法大致相同,我接下来会就如何登录雪球并获取调仓数据这个问题做详细说明。其他数据的获取我只简要写个思路,以后若是有空并且有必要我会再另写博客说明。

↑↑↑ 尚未登录时无法查看详细仓位调整记录

 ↑↑↑ 登陆后可以查看详细仓位的调整记录

(1)获取雪球优质组合

股票组合都是由雪球用户自主创建的,为了寻找优质组合而去试图遍历所有的组合寻找优质组合显然是缺乏目的性且效率低下的做法。因此我选择从那些拥有大量粉丝的大V入手,一方面大V的拥有优质组合的概率很高,而且大V的调仓记录比起一般的散户更值得机器去学习。将筛选优质组合转化为先找出“优质”用户(事实上今年七月份雪球还提供了按照日、月、年收益排行的组合检索系统,后来这检索系统莫名其妙地就消失了(눈_눈))。

好在股票组合数据爬取之前我就已经通过广度优先搜索(BFS)算法(即通过粉丝与关注建立雪球用户之间的社交网络,从一个大V出发广度优先搜索整个网络图)爬取雪球用户个人数据的方式建立了一个近200万雪球用户个人信息数据库(雪球网目前的用户数量大约是1200万,BFS到后期更新速度实在是太慢就停了)。通过以粉丝数对用户进行排序,从高到低依次获取并检验用户创建的组合是否满足优质判定(比如以年化收益大于阈值为条件)并对优质组合进行记录。

↑↑↑ 用户粉丝请求头(进入请求网址即可获取相应信息)

↑↑↑ 用户关注请求头(进入请求网址即可获取相应信息)

↑↑↑ 用户组合请求头(进入请求网址即可获取相应信息)

我提一下可能爬虫中会遇到的问题。爬取用户个人信息一般不会有任何问题(反正我没有伪装连续爬了几天一次都没被封禁过IP),但是在获取股票组合信息时往往容易被封禁IP,我这里给一个从西刺免费IP代理网站上获取第一页免费IP代理(一页共计100个IP地址,但是对于一般的爬虫一般只有前40个可能有用,而且这个IP列表更新的很快)的函数(使用urllib.request模块)。伪装IP的方法网上很容易查找到,我建议用requests.Session类中对proxies属性的伪装,它比较简单而且爬虫适应性好。另外就是伪装IP时一定要给自己的IP地址末尾添加个“:8080”之类的端口号,我第一次伪装因为不知道要添加端口一直伪装失败。

firefoxHead = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0"}
IPRegular = r"(([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5]).){3}([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5])"
def parseIPList(url="http://www.xicidaili.com/"):						 # 获取西刺免费代理IP首页的所有IP(这个用单纯的request请求是无法获取页面信息的)
	IPs = []
	request = urllib.request.Request(url,headers=firefoxHead)
	response = urllib.request.urlopen(request)
	soup = BeautifulSoup(response,"lxml")								 # 这里必须使用lxml来解析HTML
	tds = soup.find_all("td")
	for td in tds:
		string = str(td.string)
		if re.search(IPRegular,string):
			IPs.append(string)
	return IPs

↑↑↑ 西刺免费IP代理获取函数

 (2)获取股票组合的净值历史数据

净值数据在网页上的体现其实就是那张收益走势图,通过F12监听可以获取净值数据的来源。请求网址从“&since=...”之后的部分删除进行访问即可获取该股票组合全部的历史数据。

↑↑↑ 股票组合历史净值数据请求头(进入请求网址即可获取相应信息)

↑↑↑ 股票净值数据json预览

(3)登录雪球并获取股票组合的调仓历史数据

未登录状态下,当我们试图请求访问调仓记录时显示状态码为400,表明请求错误 ↓↓↓

我在没有解决登录问题前曾经想了个蠢办法,就是在浏览器上手动登录一次并调整到该页面获取请求头信息,复制一份请求头中的cookie信息添加到requests.Session的headers中进行爬虫。这方法确实非常捞,但是其实是可行的,只是这个cookie寿命很短,过一段时间就会重回HTTP400的状态从而无法持久获取信息。

在这里我顺便提一下,可能有人觉得为什么我不用http.client和http.cookiejar建立cookieJar保存cookie信息来实现访问。在我解决登录问题前我也查找过一些如何绕开雪球登录验证的方法http://www.cnblogs.com/my8100/p/7271564.html,这个链接的博主算是写得比较确实详细的了。大部分方法都是利用urllib2(对应py3的urllib.request),cookielib(对应py3的http.cookiejar)与httplib(对应py3的http.client)三个模块进行操作。但是就我尝试的情况来看,利用urllib.request模块中的urlopen实现访问请求相较于requests.Session模块的session.get实现访问请求在稳定度上要差太多,前者经常会出现HTTP40x Bad Request的错误或者返回结果就是冷冰冰的“你的IP地址是:xxx.xxx.xxx.xxx”。我在尝试上述链接博客中的代码时一直被困在这两种返回结果,到最后我连在浏览器上都无法登录雪球。当然如果有朋友正在使用py2的话也可以尝试一下上述链接博客中的代码,我觉得不行的原因主要可能还是雪球网站反爬虫机制的更新而非python版本的问题。

言归正传,我们先来研究一下雪球的登录界面。几年前雪球还是有那种在网页静态form格式的登录端口https://xueqiu.com/user/login,当然这个链接现在已经不能用于登录了。目前的有效的登录界面只有通过网页弹出式浮动窗口登录,登录方式为账号登录与短信验证登录(微信,QQ,微博方式没有试过就不谈口胡了)。

↑↑↑ 账号登录界面

↑↑↑ 短信验证登录界面

先说账号登录,账号登录的一个最大的难点在于硬刚验证码。我强烈建议各位朋友不要跟验证码过不去。今年七八月份的时候验证方式还是从图片中依次点击隐藏的汉字。九月份的时候就已经变成下面的滑动验证方式了。鬼知道以后会不会放几张白百合王珞丹的照片上来给你选,硬刚验证码实在是件吃力不讨好的方法 ↓↓↓

虽然我没有成功通过账号登录的方式模拟登录进雪球,但是为了给其他可能有兴趣尝试的小伙伴一些Tips我简要提一下我的经历与遇到的问题。

用{username:123456,password:123456}登录一次监听抓包我们发现登录时POST的URL是https://xueqiu.com/snowman/login,这里面有一个坑,大家点进这个链接就会发现这个URL被雪球HTTP30x重定向到一个奇怪的用户个人空间中去了。但是其实这个重定向并不会影响你爬虫时利用session.post(url,formdata)形式提交表单数据,只是如果你没有处理验证码,在你试图爬取股票组合调仓记录历史数据时在返回的错误信息中是会有相应的Error编号告诉你“图形验证码错误”之类的信息。室友告诉我导师做这个爬虫登录的时候是通过将这个登录界面直接在python运行时弹出,然后可以手动处理验证,然而我尝试了很多次并没有成功。我尝试用的方法是用selenium的Firefox浏览器驱动,我可以做到把登录浮动窗口点击出来,但是如果我试图去手动提交登录信息与通过验证码后,创建的webdriver.Firefox()对象就会因为我的手动操作而失效。下面这段代码可以将登录界面点击出来,但是然后就没有然后了↓↓↓

from selenium import webdriver

b = webdriver.Firefox()	
b.maximize_window()									
b.get("https://xueqiu.com")
b.find_element_by_class_name("nav__login__btn").click()

 ↑↑↑ 点击登录按钮并成功验证后抓包情况

↑↑↑ POST表单数据

在账号登录方式上死磕了一阵子后心灰意冷的我只得转而使用验证码登录。使用手机号为10000000000进行一次模拟可以发现发送验证码其实是对请求网址做一个POST请求,表单数据为86(表示地区中国)和10000000000(手机号码)

↑↑↑ 发送验证码的消息头

 ↑↑↑ 发送验证码的POST参数

def loginEX(session):													 # 登录(手动)
	codeURL = "https://xueqiu.com/account/sms/send_verification_code.json"
	loginURL = "https://xueqiu.com/snowman/login"
	formData = {														 # 登录时使用的表单
		"areacode":"86",
		"remember_me":"true",
		"telephone":"你的手机号码",
	}		
	codeData = {														 # 获取验证码时提交的表单
		"areacode":"86",
		"telephone":"你的手机号码",
	}
	session.post(codeURL,codeData)										 # 发送验证码
	formData["code"] = input("请输入验证码:")								 # 获取验证码
	r = session.post(loginURL,formData)									 # 手机验证码登录

↑↑↑ 手动输入验证码登录代码

有些朋友可能觉得手动输入验证码的方式似乎有点麻烦,我一开始也是这么想的,所以打算用python去读取手机短信,但是实际情况并不是那么容易,似乎监听手机短信对于python来说是一件很麻烦的事情。后来我想了一个蠢办法,通过对手机截屏在截取到然后利用pytessoract库进行数字识别即可。笔者用的是华为麦芒5手机,因此如果其他朋友也想用下面这段代码很多坐标数据都需要重新修改。实现自动获取验证码的代码 ↓↓↓

ADBC = [																 # ADB常用语句
	"adb shell input keyevent {}",										 # keyevent事件 
	"adb shell input swipe {} {} {} {}",								 # 滑动事件						
	"adb shell screencap -p /sdcard/{}",								 # 截屏事件
	"adb pull /sdcard/{}",												 # 加载图片至电脑
	"adb shell dumpsys window policy|findstr mShowingLockscreen",		 # 判断是否黑屏
]

def getCode(rawImage="raw.png",cropImage="crop.png"):					 # 获取短信验证码
	if os.path.isfile(rawImage):
		os.remove(rawImage)
	if os.path.isfile(cropImage):
		os.remove(cropImage)
	if os.popen(ADBC[4]).read()[23]=="t":								 # ADBC[4]的返回结果第23个字符是t(rue)表明是锁屏状态,是f(alse)表明不是锁屏状态(一般来说只要来了短信)
		os.system(ADBC[1].format(0,960,1080,960))						 # 模拟划开屏幕得到锁屏界面(我已经把锁屏密码去掉了,所以直接就是当前页面)
		time.sleep(0.5)													 # 暂停0.5秒是为了让屏幕稳定下来(否则可能截到花屏)
	os.system(ADBC[2].format(rawImage))									 # 在手机中截屏
	os.system(ADBC[3].format(rawImage))									 # 将截屏图片导入当前计算机目录
	Image.open(rawImage).load()											 # 写入图片文件
	""" 将验证码部分裁剪出来并进行数字识别 """
	raw = cv2.imread(rawImage)
	crop = raw[305:360,350:455]
	cv2.imwrite(cropImage,crop)
	text = pytesseract.image_to_string(crop,lang='chi_sim')
	return text

def login(session):														 # 登录(自动)
	codeURL = "https://xueqiu.com/account/sms/send_verification_code.json"
	loginURL = "https://xueqiu.com/snowman/login"
	formData = {														 # 登录时使用的表单
		"areacode":"86",
		"remember_me":"true",
		"telephone":"你的手机号码",
	}		
	codeData = {														 # 获取验证码时提交的表单
		"areacode":"86",
		"telephone":"你的手机号码",
	}
	session.post(codeURL,codeData)										 # 发送验证码
	time.sleep(10)														 # 等待收到验证码(以后可以优化)
	formData["code"] = getCode()										 # 获取验证码
	r = session.post(loginURL,formData)									 # 手机验证码登录

股票调仓记录历史数据HTML预览 ↑↑↑

这里提醒一下雪球短信验证码登录一天只能使用5次,超过5次就无法再发送短信验证码了。因此在调试上非常的不方便,实在不行就发动家人朋友都在雪球上注册个账号这样一天可以多调试几次代码。

然后我们就可以快乐地爬取调仓记录的历史数据啦!

其实并不快乐,如果你访问过快就很容易被雪球封禁。目前我通过上面伪装IP的方法大约每隔6.5秒请求一次可以比较稳定的访问URL,如果被封禁则需要等待大约600秒可以恢复访问状态。具体的方法与上述类似,这里就直接上代码了 ↓↓↓

def parsePortfolioHistory(session,symbol,fileRoute,interval,maxCount=50):# 获取指定编号的组合调仓历史
	histroyURL = "https://xueqiu.com/cubes/rebalancing/history.json?cube_symbol={}&count={}&page={}"
	html = session.get(histroyURL.format(symbol,1,1)).text
	print(html)
	if html[2]=="e":													 # 第二个字符是e表示出现了error(直接返回)
		return False
	else:
		tempIndex = html.find("\"totalCount\":")						 # totalCount表示调仓总次数
		dotIndex = html.find(",",tempIndex)
		total = int(html[tempIndex+13:dotIndex])						 # 获取调仓总次数
		log = open("Histroy\\{}".format(fileRoute),"a")
		page = math.ceil(total/maxCount)
		print("一共有{}页".format(page))
		page = min(page,50)												 # 说实话这个50页上限是个坑(无论一页放多少条调仓记录,最终只能最多50页,一页上限是50条)
		for i in range(page):
			html = session.get(histroyURL.format(symbol,maxCount,i+1)).text
			if html[2]=="e":											 # 表明爬取一只组合的中途出错了
				log.close()
				os.remove("Histroy\\{}".format(fileRoute))				 # 删除文件
				return False		
			time.sleep(random.uniform(0,interval))
			try:
				log.write("{}\n".format(html))
			except:
				f = open("Error.txt","a")
				f.write("{}\t{}\n".format(symbol,datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
				f.close()
				return True
		log.close()
	return True

def main():																 # 主函数
	IPs = parseIPList()
	f = open("P.txt","r")
	symbols = f.read().split("\n")[:-1]									 # 获取组合列表
	mainURL = "https://xueqiu.com"
	session = requests.Session()
	session.headers = firefoxHead.copy()
	session.get(mainURL)												 # 定位雪球网主页
	loginEX(session)
	index = 0
	requestInterval = 6.5
	resetInterval = 600
	
	flag1 = False
	
	while index<len(symbols):
		print("正在获取{}的调仓记录".format(symbols[index]))
		proxy = {"http":"{}:8080".format(IPs[random.randint(0,40)])}	 # 设置代理IP
		session.proxies = proxy
		flag = parsePortfolioHistory(session,"{}".format(symbols[index]),"{}.txt".format(symbols[index]),requestInterval)
		if flag:
			index += 1
			if flag1:
				flag1 = False						
		else:															 # 表明访问过于频繁
			print("-------------访问过于频繁,爬虫中止!-------------")
			f1 = open("Error.txt","a")
			f1.write("{}中止\t{}\n".format(symbols[index],datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
			f1.close()
			if flag1:
				resetInterval += 50
			else:
				flag1 = True
				requestInterval += 0.5
				f2 = open("requestInterval.txt","a")
				f2.write("当前requestInterval为{}\t{}\n".format(requestInterval,datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))	
				f2.close()
			time.sleep(resetInterval)
			f3 = open("resetInterval.txt","a")
			f3.write("当前resetInterval为{}\t{}\n".format(resetInterval,datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
			f3.close()

 

 问题总结

雪球网用户股票组合数据爬虫除了上面已经提到的一些问题外,我再在这里提一些其他的细节问题:

1、股票组合调仓记录数据保存在"https://xueqiu.com/cubes/rebalancing/history.json?cube_symbol={s}&count={c}&page={p}".format(s,c,p)格式的URL中,s表示股票组合编号,c表示一页上调仓数据的条数,p为页数。注意c和p最多只能取到50,也就是说最多可以获取到2500条数据。事实上有不少组合调仓非常频繁,有时候一日之内会调很多次,因此一些组合是无法获取到全部调仓数据的。

2、雪球网的股票组合编号完全按照创建时间顺序排列(至少沪深组合是这样,其他的我没具体研究),因此如果需要调取一个时间段内的组合完全可以根据编号大小排序来实现。这里给出一些参考结点:

组合编码    创建日期
ZH100000    2015.01.03
ZH200000    2015.02.16
ZH300000    2015.04.04
ZH400000    2015.05.08
ZH500000    2015.06.06
ZH600000    2015.07.19
ZH700000    2015.10.10
ZH800000    2016.01.25
ZH900000    2016.06.30
ZH1000000    2016.12.24
ZH1100000    2017.06.23
ZH1200000    2017.11.17
ZH1300000    2018.03.22
ZH1350000    2018.06.12
ZH1580000    2018.08.04

净值1.0000即可跑赢75%的组合
净值1.0260即可跑赢80%的组合

3、文字编码问题。这个问题不是很大,但是有时候很恶心。当我们把股票组合调仓记录的HTML记录到文本中后可能会出现无法读取的问题。无论在读取文本时设置encoding参数是GBK还是UTF-8都无法读取,可能的办法是先编码再解码,这个需要尝试很多次才能找到正确的编码解码组合。我对HTML上的文本一般是用ISO-8859-1编码再用UTF-8或者GBK解码,但是这次大约10000个组合中只出现了一个有编码问题无法读取,我用了很多编码解码组合都没有很好的处理掉。因为数量确实太少了,所以我暂时就忽略这个问题了。

 

最后上全部代码 ↓↓↓

# -*- coding:UTF-8 -*-
"""
	作者:囚生CY
	平台:CSDN
	时间:2018/09/20
	转载请注明原作者
	创作不易,仅供分享
"""
import os
import re
import cv2
import math
import time
import urllib
import random
import hashlib
import pymysql
import datetime
import requests
import threading
import pytesseract
import pandas as pd
from PIL import Image
import multiprocessing as mp
from bs4 import BeautifulSoup

firefoxHead = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0"}
IPRegular = r"(([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5]).){3}([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5])"
ADBC = [																 # ADB常用语句
	"adb shell input keyevent {}",										 # keyevent事件 
	"adb shell input swipe {} {} {} {}",								 # 滑动事件						
	"adb shell screencap -p /sdcard/{}",								 # 截屏事件
	"adb pull /sdcard/{}",												 # 加载图片至电脑
	"adb shell dumpsys window policy|findstr mShowingLockscreen",		 # 判断是否黑屏
]
def parseIPList(url="http://www.xicidaili.com/"):						 # 获取西刺免费代理IP首页的所有IP(这个用单纯的request请求是无法获取页面信息的)
	IPs = []
	request = urllib.request.Request(url,headers=firefoxHead)
	response = urllib.request.urlopen(request)
	soup = BeautifulSoup(response,"lxml")								 # 这里必须使用lxml来解析HTML
	tds = soup.find_all("td")
	for td in tds:
		string = str(td.string)
		if re.search(IPRegular,string):
			IPs.append(string)
	return IPs

def getCode(rawImage="raw.png",cropImage="crop.png"):					 # 获取短信验证码
	if os.path.isfile(rawImage):
		os.remove(rawImage)
	if os.path.isfile(cropImage):
		os.remove(cropImage)
	if os.popen(ADBC[4]).read()[23]=="t":								 # ADBC[4]的返回结果第23个字符是t(rue)表明是锁屏状态,是f(alse)表明不是锁屏状态(一般来说只要来了短信)
		os.system(ADBC[1].format(0,960,1080,960))						 # 模拟划开屏幕得到锁屏界面(我已经把锁屏密码去掉了,所以直接就是当前页面)
		time.sleep(0.5)													 # 暂停0.5秒是为了让屏幕稳定下来(否则可能截到花屏)
	os.system(ADBC[2].format(rawImage))									 # 在手机中截屏
	os.system(ADBC[3].format(rawImage))									 # 将截屏图片导入当前计算机目录
	Image.open(rawImage).load()											 # 写入图片文件
	""" 将验证码部分裁剪出来并进行数字识别 """
	raw = cv2.imread(rawImage)
	crop = raw[305:360,350:455]
	cv2.imwrite(cropImage,crop)
	text = pytesseract.image_to_string(crop,lang='chi_sim')
	return text

def login(session):														 # 登录(自动)
	codeURL = "https://xueqiu.com/account/sms/send_verification_code.json"
	loginURL = "https://xueqiu.com/snowman/login"
	formData = {														 # 登录时使用的表单
		"areacode":"86",
		"remember_me":"true",
		"telephone":"你的手机号码",
	}		
	codeData = {														 # 获取验证码时提交的表单
		"areacode":"86",
		"telephone":"你的手机号码",
	}
	session.post(codeURL,codeData)										 # 发送验证码
	time.sleep(10)														 # 等待收到验证码(以后可以优化)
	formData["code"] = getCode()										 # 获取验证码
	r = session.post(loginURL,formData)									 # 手机验证码登录

def loginEX(session):													 # 登录(手动)
	codeURL = "https://xueqiu.com/account/sms/send_verification_code.json"
	loginURL = "https://xueqiu.com/snowman/login"
	formData = {														 # 登录时使用的表单
		"areacode":"86",
		"remember_me":"true",
		"telephone":"你的手机号码",
	}		
	codeData = {														 # 获取验证码时提交的表单
		"areacode":"86",
		"telephone":"你的手机号码",
	}
	session.post(codeURL,codeData)										 # 发送验证码
	formData["code"] = input("请输入验证码:")								 # 获取验证码
	r = session.post(loginURL,formData)									 # 手机验证码登录

def parsePortfolioNet(session,symbol):									 # 获取指定编号的组合净值情况
	netURL = "https://xueqiu.com/cubes/nav_daily/all.json?cube_symbol={}"
	html = session.get(netURL.format(symbol)).text
	data = html.split("},{")
	for i in data:
		print(i)

def parsePortfolioHistory(session,symbol,fileRoute,interval,maxCount=50):# 获取指定编号的组合调仓历史
	histroyURL = "https://xueqiu.com/cubes/rebalancing/history.json?cube_symbol={}&count={}&page={}"
	html = session.get(histroyURL.format(symbol,1,1)).text
	print(html)
	if html[2]=="e":													 # 第二个字符是e表示出现了error(直接返回)
		return False
	else:
		tempIndex = html.find("\"totalCount\":")						 # totalCount表示调仓总次数
		dotIndex = html.find(",",tempIndex)
		total = int(html[tempIndex+13:dotIndex])						 # 获取调仓总次数
		log = open("Histroy\\{}".format(fileRoute),"a")
		page = math.ceil(total/maxCount)
		print("一共有{}页".format(page))
		page = min(page,50)												 # 说实话这个50页上限是个坑(无论一页放多少条调仓记录,最终只能最多50页,一页上限是50条)
		for i in range(page):
			html = session.get(histroyURL.format(symbol,maxCount,i+1)).text
			if html[2]=="e":											 # 表明爬取一只组合的中途出错了
				log.close()
				os.remove("Histroy\\{}".format(fileRoute))				 # 删除文件
				return False		
			time.sleep(random.uniform(0,interval))
			try:
				log.write("{}\n".format(html))
			except:
				f = open("Error.txt","a")
				f.write("{}\t{}\n".format(symbol,datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
				f.close()
				return True
		log.close()
	return True
	

def main():																 # 主函数
	IPs = parseIPList()
	f = open("P.txt","r")												 # 这个文件包含了所有需要爬取的组合编码(需要你自己获取)
	symbols = f.read().split("\n")[:-1]									 # 获取组合列表
	mainURL = "https://xueqiu.com"
	session = requests.Session()
	session.headers = firefoxHead.copy()
	session.get(mainURL)												 # 定位雪球网主页

	index = 0
	requestInterval = 6.5
	resetInterval = 600
	
	flag1 = False
	
	while index<len(symbols):
		print("正在获取{}的调仓记录".format(symbols[index]))
		proxy = {"http":"{}:8080".format(IPs[random.randint(0,40)])}	 # 设置代理IP
		session.proxies = proxy
		flag = parsePortfolioHistory(session,"{}".format(symbols[index]),"{}.txt".format(symbols[index]),requestInterval)
		if flag:
			index += 1
			if flag1:
				flag1 = False						
		else:															 # 表明访问过于频繁
			print("-------------访问过于频繁,爬虫中止!-------------")
			f1 = open("Error.txt","a")
			f1.write("{}中止\t{}\n".format(symbols[index],datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
			f1.close()
			if flag1:
				resetInterval += 50
			else:
				flag1 = True
				requestInterval += 0.5
				f2 = open("requestInterval.txt","a")
				f2.write("当前requestInterval为{}\t{}\n".format(requestInterval,datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))	
				f2.close()
			time.sleep(resetInterval)
			f3 = open("resetInterval.txt","a")
			f3.write("当前resetInterval为{}\t{}\n".format(resetInterval,datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
			f3.close()
			
if __name__ == "__main__":
	main()

感谢阅读,共同进步!

 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页