使用nodejs爬取和讯网高管增减持数据
为了抓取和讯网高管增减持的数据,首先得分析一下数据的来源:
网址: http://stockdata.stock.hexun.com/ggzjc/history.shtml
使用chrome开发者工具,可以发现在切换到第二页时,浏览器向下述地址发起了网络访问请求:
http://stockdata.stock.hexun.com/ggzjc/data/ChangeHistory.aspx?count=30&page=2&callback=hxbase_json5
分析一下上述链接, count表示一页返回的结果数目,page代表页码数,callback表示回调函数的名称.
以下是发起上述URL对应的网络请求返回的数据:
很明显,这是一段javascript代码,不是json数据,无法使用python进行直接解析.为了加快项目进度,减少耦合,可以使用nodejs一步完成,不用将这个数据爬取分为抓取和解析两个步骤.
为了加快爬取速度,我们设置每发起一次请求,返回1000条数据,在给定页码范围的情况下,就可以生成由所有链接构成的数组:
function get_url_array(start, end) {var url_template = "http://stockdata.stock.hexun.com/ggzjc/data/ChangeHistory.aspx?count=1000&page=%d&callback=hxbase_json5"var util = require("util")var array = new Array()for (var i = start; i <= end; i++) {var url_one = util.format(url_template, i + 1)array.push(url_one)}return array }
对于给定链接,获取该链接的数据并将其转换为javascript对象,取出其中有价值的数据list,对应函数:
function get_data_from_url(url) {var request = require('sync-request');var user_agent_list =['Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36','Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1','Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50','Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1','Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56',"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 ","Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 ","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 ","Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 ","Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 ","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 ","Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 ","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 ","Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 ","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 ","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 ","Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 ","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 ","Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 ","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36"]var pos =randomIntRange(0,user_agent_list.length-1)// 增加user-agentvar res = request('GET', url, {'headers': {'user-agent':user_agent_list[pos],'Host': 'stockdata.stock.hexun.com','Referer': 'http://stockdata.stock.hexun.com/ggzjc/history.shtml'},retry : true,retryDelay: 10000,maxRetries: 5,timeout:200000});var buf = res.getBody()var iconv = require("iconv-lite")// 使用gb2312编码方式var data_str = iconv.decode(buf, 'gb2312')data_str = data_str.replace(/上海市浦东新区公共交通投资发\\/g,"上海市浦东新区公共交通投资发")var data_list = eval(data_str)return data_list.list }
这里面有一个小坑,在大概处理第14个链接的时候,服务器返回的数据并不是正确的javascript脚本,在此处有错误:
//wrong ! changePeopleTitle: '上海市浦东新区公共交通投资发\' //right changePeopleTitle: '上海市浦东新区公共交通投资发'
就因为多了一个转义符号,导致整个语句有问题,不能正确利用eval函数进行转换.这背后肯定是某位mm手残的结果.所以需要对这个bug特殊处理,对应上述代码的标红部分.这个抓取程序需要正确设置user-agent,为了防止被卡,我设置了user-agent池,利用random函数随机选取user-agent.
//随机生成范围在low,high之间的随机数 function randomIntRange (low, high) {return Math.floor(Math.random() * (high - low + 1) + low); }
为了解析javascript语句,需要设置和请求对应的回调函数,如下:
function hxbase_json5(str) {var data = eval(str)return data }
解析javascript对象并将其存入数据库的操作定义在函数save_data_to_mysql() 中,其中利用了sequelize的orm模型来简化实现.
function save_data_to_mysql() {var Sequelize = require('sequelize')var sleep = require("sleep")var sequelize = new Sequelize('dbname','root','passwd',{'dialect': 'mysql','host': '127.0.0.1','port': 3306,define: {charset: 'utf8',timestamps: false//不定义时间戳 }})//高管增减持var Ggzjc = sequelize.define('table_name', {'stock_code': {//股票代码'type': Sequelize.STRING,'allowNull': false,'unique': false},'stock_name': {//股票名称'type': Sequelize.STRING,'allowNull': false,'unique': false},'changeDate': {//变动日期'type': Sequelize.DATEONLY,'allowNull': true},'noticeDate': {//公告日期'type': Sequelize.DATEONLY,'allowNull': true},'changeNum': {// 变动数量 万股'type': Sequelize.DOUBLE,'allowNull': true},'averagePrice': {//均价'type': Sequelize.DOUBLE,'allowNull': true},'price': {//金额'type': Sequelize.DOUBLE,'allowNull': true},'shareHoldingNum': {//变动后持股数目'type': Sequelize.DOUBLE,'allowNull': true},'changeRatio': {//变动人变动比'type': Sequelize.DOUBLE,'allowNull': true},'circulationCapitalRatio': {//占流通股本比例'type': Sequelize.DOUBLE,'allowNull': true},'changeWay': {//变动方式'type': Sequelize.STRING,'allowNull': true},'changePeople': {//股份变动人'type': Sequelize.STRING,'allowNull': true},'changePeopleTitle': {//相关董事高管'type': Sequelize.STRING,'allowNull': true},'duties': {//职务'type': Sequelize.STRING,'allowNull': true},'relation': {//关系'type': Sequelize.STRING,'allowNull': true},'industry': {//行业'type': Sequelize.STRING,'allowNull': true},})Ggzjc.sync({force: true}).then(function () {var url_array = get_url_array(1, 58)for (var i = 0; i < url_array.length; i++) {var data = get_data_from_url(url_array[i])sleep.usleep(200000)print(i + 1)print('complete!')for (var j = 0; j < data.length; j++) {var changeDate = '20' + data[j].changeDatevar noticeDate = '20' + data[j].noticeDatevar str_array = data[j].stockName.split("(")var stock_name = str_array[0]str_array = str_array[1].split(")")var stock_code = str_array[0]var changeNum = get_content_from_html(data[j].changeNum)if (changeNum != null) {if (changeNum == "")changeNum = nullelsechangeNum = parseFloat(changeNum)}var averagePrice = data[j].averagePriceif (averagePrice == ' ')averagePrice = nullelseaveragePrice = parseFloat(averagePrice)var price = get_content_from_html(data[j].price)if (price != null) {if (price == '')price = nullelseprice = parseFloat(price)}var shareHoldingNum = data[j].shareHoldingNumif (shareHoldingNum == ' ')shareHoldingNum = nullelseshareHoldingNum = parseFloat(shareHoldingNum)var changeRatio = data[j].changeRatioif (changeRatio == ' ')changeRatio = nullelsechangeRatio = parseFloat(changeRatio)var circulationCapitalRatio = data[j].circulationCapitalRatioif (circulationCapitalRatio == ' ')circulationCapitalRatio = nullelsecirculationCapitalRatio = parseFloat(circulationCapitalRatio)var changeWay = data[j].changeWayif (changeWay == ' ')changeWay = nullvar changePeople = data[j].changePeople// console.log(data[j].changePeople)if (changePeople == ' ')changePeople = nullvar changePeopleTitle = data[j].changePeopleTitleif (changePeopleTitle == ' ')changePeopleTitle = nullvar duties = get_content_from_html(data[j].duties)if (duties != null && duties == '')duties = nullvar relation = get_content_from_html(data[j].relation)if (relation != null && relation == '')relation = nullvar industry = data[j].industryif (industry == ' ')industry = nullvar one = Ggzjc.build({'stock_code': stock_code,'stock_name': stock_name,'changeDate': changeDate,'noticeDate': noticeDate,'changeNum': changeNum,'averagePrice': averagePrice,'price': price,'shareHoldingNum': shareHoldingNum,'changeRatio': changeRatio,'circulationCapitalRatio': circulationCapitalRatio,'changeWay': changeWay,'changePeople': changePeople,'changePeopleTitle': changePeopleTitle,'duties': duties,'relation': relation,'industry': industry})one.save()}}}) }
需要解析并获取html标签<tag>content</tag>中的content,利用正则表达式取出><中间的文本就可以了.
function get_content_from_html(str) {var pattern = />[\s\S]+?</gvar res = str.match(pattern)if (res == null) {return null}var result = res[0]return result.slice(1, result.length - 1) }
done!
附注:
借助python execjs和pandas,我实现了以更加优美的姿势爬取上述内容,代码详见我的github:
https://github.com/zhoudayang/get_hexun
转载于:https://www.cnblogs.com/zhoudayang/p/5489969.html
使用nodejs爬取和讯网高管增减持数据相关推荐
- 用python爬取高考网历年高考分数线将数据放入MySQL并绘制图表
用python爬取高考网历年高考分数线 # 导入爬虫的库 import requests from lxml import etree # 导入画图库 from pyecharts.charts im ...
- scrapy爬取当当网Python图书的部分数据
1.下载scrapy框架 pip install scrapy 2.在E盘下创建一个文件夹scrapy01,在命令行窗体中进入该文件夹 3.创建项目:scrapy startproject 项目名 s ...
- python爬虫 爬取 豆瓣网 搜索结果 同城活动 数据
主要使用的库: requests:爬虫请求并获取源码 re:使用正则表达式提取数据 json:使用JSON提取数据 pandas:使用pandans存储数据 bs4:网页代码解析 以下是源代码: #! ...
- 在当当买了python怎么下载源代码-爬虫实战:爬取当当网所有 Python 书籍
来源:公众号-极客猴 出处: 本文主要讲解如何利用urllib.re.BeautifulSoup 这几个库去实战,爬取当当网所有 Python 书籍. 1 确定爬取目标 任何网站皆可爬取,就看你要不要 ...
- [python爬虫] BeautifulSoup和Selenium简单爬取知网信息测试
作者最近在研究复杂网络和知识图谱内容,准备爬取知网论文相关信息进行分析,包括标题.摘要.出版社.年份.下载数和被引用数.作者信息等.但是在爬取知网论文时,遇到问题如下: 1.爬取内容总为空,其原因 ...
- Scarpy爬取当当网书籍
目录 1:Scarpy (1) Scrapy是什么: (2)安装scrapy: 2.scrapy项目的创建以及运行 1.创建scrapy项目: 2.项目组成: 3.创建爬虫文件 4.爬虫文件的基本组 ...
- Python爬取豆瓣网影评展示
Python爬取豆瓣网影评展示 需要的库文件 requests beautifulsoup wordcloud jieba matplotlib 本文思想 1.访问指定的网页 #获取指定url的内容 ...
- Python 爬虫第三步 -- 多线程爬虫爬取当当网书籍信息
XPath 的安装以及使用 1 . XPath 的介绍 刚学过正则表达式,用的正顺手,现在就把正则表达式替换掉,使用 XPath,有人表示这太坑爹了,早知道刚上来就学习 XPath 多省事 啊.其实我 ...
- 记录一次nodejs爬取《17吉他》所有吉他谱
记录一次nodejs爬取<17吉他>所有吉他谱(只探讨技术) 突然就想扒一下吉他谱了,说做就做哈哈,中间也是没有想象中的顺利啊,出现了各种意想不到的坑,包括老生常谈的nodejs异步写法, ...
最新文章
- 【TensorFlow2.0】(6) 数据统计,范数、最值、求和、均值、最值位置、唯一值、张量比较
- 郭为:大数据时代的企业管理挑战
- 38 JavaScript中的this指向问题
- 5G+AI:影响未来的新趋势
- 北京 10 年,难说再见!
- 利用逆矩阵解线性方程组_QR方法求解矩阵所有特征值(一)
- 2017西南计算机数学基础,[0838]《计算机数学基础》西南大学 2017 秋学期 计算机专业 作业题目及参考答案资料讲解.docx...
- 异常规范之异常的概念
- 【bzoj3033】太鼓达人 DFS欧拉图
- 如何open一个新tab页面
- STM32——库函数开发小结
- 尴尬:原来java中有两个 ModelAndView类
- 深度优先搜索——单词方阵(洛谷 P1101)
- Window系统下 MongoDB 下载 和 安装
- The Apache Software Foundation Announces Apache® Zeppelin™ as a Top-Level Project
- Base64、32、16 编码解析
- 程序员都知道的二维码扫码登录的底层原理
- 【人月神话】第二章:人月神话
- 基于51单片机的DS1302实时时钟程序
- 哪些神句拯救了你的英文邮件?
热门文章
- 【FZU】Problem 2181 快来买肉松饼 点双连通
- Google Earth Engine(GEE)——全球洪水数据库 v1 (2000-2018年)
- 简历模板python爬虫
- 库文件搜索路径及GCC搜索路径总结
- vue学习【非父子组件传值问题(Bus/总线/观察者模式)】
- 2021年中国海图记录器市场趋势报告、技术动态创新及2027年市场预测
- 腾讯汤道生:2020年加大投入产业互联网生态建设
- python入门2——基础语法2——字符串详解
- edge浏览器如何把网页放到桌面_win10edge怎么放在桌面
- python自动发邮件报554错误_python3 使用smtplib发送邮件错误554