为了抓取和讯网高管增减持的数据,首先得分析一下数据的来源:

  网址: 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 == '&nbsp;')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 == '&nbsp;')shareHoldingNum = nullelseshareHoldingNum = parseFloat(shareHoldingNum)var changeRatio = data[j].changeRatioif (changeRatio == '&nbsp;')changeRatio = nullelsechangeRatio = parseFloat(changeRatio)var circulationCapitalRatio = data[j].circulationCapitalRatioif (circulationCapitalRatio == '&nbsp;')circulationCapitalRatio = nullelsecirculationCapitalRatio = parseFloat(circulationCapitalRatio)var changeWay = data[j].changeWayif (changeWay == '&nbsp;')changeWay = nullvar changePeople = data[j].changePeople// console.log(data[j].changePeople)if (changePeople == '&nbsp;')changePeople = nullvar changePeopleTitle = data[j].changePeopleTitleif (changePeopleTitle == '&nbsp;')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 == '&nbsp;')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爬取和讯网高管增减持数据相关推荐

  1. 用python爬取高考网历年高考分数线将数据放入MySQL并绘制图表

    用python爬取高考网历年高考分数线 # 导入爬虫的库 import requests from lxml import etree # 导入画图库 from pyecharts.charts im ...

  2. scrapy爬取当当网Python图书的部分数据

    1.下载scrapy框架 pip install scrapy 2.在E盘下创建一个文件夹scrapy01,在命令行窗体中进入该文件夹 3.创建项目:scrapy startproject 项目名 s ...

  3. python爬虫 爬取 豆瓣网 搜索结果 同城活动 数据

    主要使用的库: requests:爬虫请求并获取源码 re:使用正则表达式提取数据 json:使用JSON提取数据 pandas:使用pandans存储数据 bs4:网页代码解析 以下是源代码: #! ...

  4. 在当当买了python怎么下载源代码-爬虫实战:爬取当当网所有 Python 书籍

    来源:公众号-极客猴 出处: 本文主要讲解如何利用urllib.re.BeautifulSoup 这几个库去实战,爬取当当网所有 Python 书籍. 1 确定爬取目标 任何网站皆可爬取,就看你要不要 ...

  5. [python爬虫] BeautifulSoup和Selenium简单爬取知网信息测试

    作者最近在研究复杂网络和知识图谱内容,准备爬取知网论文相关信息进行分析,包括标题.摘要.出版社.年份.下载数和被引用数.作者信息等.但是在爬取知网论文时,遇到问题如下:   1.爬取内容总为空,其原因 ...

  6. Scarpy爬取当当网书籍

    目录 1:Scarpy (1)  Scrapy是什么: (2)安装scrapy: 2.scrapy项目的创建以及运行 1.创建scrapy项目: 2.项目组成: 3.创建爬虫文件 4.爬虫文件的基本组 ...

  7. Python爬取豆瓣网影评展示

    Python爬取豆瓣网影评展示 需要的库文件 requests beautifulsoup wordcloud jieba matplotlib 本文思想 1.访问指定的网页 #获取指定url的内容 ...

  8. Python 爬虫第三步 -- 多线程爬虫爬取当当网书籍信息

    XPath 的安装以及使用 1 . XPath 的介绍 刚学过正则表达式,用的正顺手,现在就把正则表达式替换掉,使用 XPath,有人表示这太坑爹了,早知道刚上来就学习 XPath 多省事 啊.其实我 ...

  9. 记录一次nodejs爬取《17吉他》所有吉他谱

    记录一次nodejs爬取<17吉他>所有吉他谱(只探讨技术) 突然就想扒一下吉他谱了,说做就做哈哈,中间也是没有想象中的顺利啊,出现了各种意想不到的坑,包括老生常谈的nodejs异步写法, ...

最新文章

  1. 【TensorFlow2.0】(6) 数据统计,范数、最值、求和、均值、最值位置、唯一值、张量比较
  2. 郭为:大数据时代的企业管理挑战
  3. 38 JavaScript中的this指向问题
  4. 5G+AI:影响未来的新趋势
  5. 北京 10 年,难说再见!
  6. 利用逆矩阵解线性方程组_QR方法求解矩阵所有特征值(一)
  7. 2017西南计算机数学基础,[0838]《计算机数学基础》西南大学 2017 秋学期 计算机专业 作业题目及参考答案资料讲解.docx...
  8. 异常规范之异常的概念
  9. 【bzoj3033】太鼓达人 DFS欧拉图
  10. 如何open一个新tab页面
  11. STM32——库函数开发小结
  12. 尴尬:原来java中有两个 ModelAndView类
  13. 深度优先搜索——单词方阵(洛谷 P1101)
  14. Window系统下 MongoDB 下载 和 安装
  15. The Apache Software Foundation Announces Apache® Zeppelin™ as a Top-Level Project
  16. Base64、32、16 编码解析
  17. 程序员都知道的二维码扫码登录的底层原理
  18. 【人月神话】第二章:人月神话
  19. 基于51单片机的DS1302实时时钟程序
  20. 哪些神句拯救了你的英文邮件?

热门文章

  1. 【FZU】Problem 2181 快来买肉松饼 点双连通
  2. Google Earth Engine(GEE)——全球洪水数据库 v1 (2000-2018年)
  3. 简历模板python爬虫
  4. 库文件搜索路径及GCC搜索路径总结
  5. vue学习【非父子组件传值问题(Bus/总线/观察者模式)】
  6. 2021年中国海图记录器市场趋势报告、技术动态创新及2027年市场预测
  7. 腾讯汤道生:2020年加大投入产业互联网生态建设
  8. python入门2——基础语法2——字符串详解
  9. edge浏览器如何把网页放到桌面_win10edge怎么放在桌面
  10. python自动发邮件报554错误_python3 使用smtplib发送邮件错误554