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

  网址: 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条数据,在给定页码范围的情况下,就可以生成由所有链接构成的数组:

  1. function get_url_array(start, end) {
  2. var url_template = "http://stockdata.stock.hexun.com/ggzjc/data/ChangeHistory.aspx?count=1000&page=%d&callback=hxbase_json5"
  3. var util = require("util")
  4. var array = new Array()
  5. for (var i = start; i <= end; i++) {
  6. var url_one = util.format(url_template, i + 1)
  7. array.push(url_one)
  8. }
  9. return array
  10. }

  对于给定链接,获取该链接的数据并将其转换为javascript对象,取出其中有价值的数据list,对应函数:

  1. function get_data_from_url(url) {
  2. var request = require('sync-request');
  3. var user_agent_list =[
  4. 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36',
  5. 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1',
  6. 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50',
  7. 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1',
  8. 'Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11',
  9. 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56',
  10. "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 ",
  11. "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 ",
  12. "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 ",
  13. "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 ",
  14. "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 ",
  15. "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 ",
  16. "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 ",
  17. "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 ",
  18. "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 ",
  19. "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 ",
  20. "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 ",
  21. "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 ",
  22. "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 ",
  23. "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 ",
  24. "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36"
  25. ]
  26. var pos =randomIntRange(0,user_agent_list.length-1)
  27. // 增加user-agent
  28. var res = request('GET', url, {
  29. 'headers': {
  30. 'user-agent':user_agent_list[pos],
  31. 'Host': 'stockdata.stock.hexun.com',
  32. 'Referer': 'http://stockdata.stock.hexun.com/ggzjc/history.shtml'
  33. },
  34. retry : true,
  35. retryDelay: 10000,
  36. maxRetries: 5,
  37. timeout:200000
  38. });
  39. var buf = res.getBody()
  40. var iconv = require("iconv-lite")
  41. // 使用gb2312编码方式
  42. var data_str = iconv.decode(buf, 'gb2312')
  43. data_str = data_str.replace(/上海市浦东新区公共交通投资发\\/g,"上海市浦东新区公共交通投资发")
  44. var data_list = eval(data_str)
  45. return data_list.list
  46. }

  这里面有一个小坑,在大概处理第14个链接的时候,服务器返回的数据并不是正确的javascript脚本,在此处有错误:

  1. //wrong !
  2. changePeopleTitle: '上海市浦东新区公共交通投资发\'
  3. //right
  4. changePeopleTitle: '上海市浦东新区公共交通投资发'

  就因为多了一个转义符号,导致整个语句有问题,不能正确利用eval函数进行转换.这背后肯定是某位mm手残的结果.所以需要对这个bug特殊处理,对应上述代码的标红部分.这个抓取程序需要正确设置user-agent,为了防止被卡,我设置了user-agent池,利用random函数随机选取user-agent.

  1. //随机生成范围在low,high之间的随机数
  2. function randomIntRange (low, high) {
  3. return Math.floor(Math.random() * (high - low + 1) + low);
  4. }

  为了解析javascript语句,需要设置和请求对应的回调函数,如下:

  1. function hxbase_json5(str) {
  2. var data = eval(str)
  3. return data
  4. }

  解析javascript对象并将其存入数据库的操作定义在函数save_data_to_mysql() 中,其中利用了sequelize的orm模型来简化实现.

  1. function save_data_to_mysql() {
  2.  
  3. var Sequelize = require('sequelize')
  4. var sleep = require("sleep")
  5. var sequelize = new Sequelize(
  6. 'dbname',
  7. 'root',
  8. 'passwd',
  9. {
  10. 'dialect': 'mysql',
  11. 'host': '127.0.0.1',
  12. 'port': 3306,
  13. define: {
  14. charset: 'utf8',
  15. timestamps: false
  16. //不定义时间戳
  17. }
  18. }
  19. )
  20. //高管增减持
  21. var Ggzjc = sequelize.define(
  22. 'table_name', {
  23. 'stock_code': {//股票代码
  24. 'type': Sequelize.STRING,
  25. 'allowNull': false,
  26. 'unique': false
  27. },
  28. 'stock_name': {//股票名称
  29. 'type': Sequelize.STRING,
  30. 'allowNull': false,
  31. 'unique': false
  32. },
  33. 'changeDate': {//变动日期
  34. 'type': Sequelize.DATEONLY,
  35. 'allowNull': true
  36. },
  37. 'noticeDate': {//公告日期
  38. 'type': Sequelize.DATEONLY,
  39. 'allowNull': true
  40. },
  41. 'changeNum': {// 变动数量 万股
  42. 'type': Sequelize.DOUBLE,
  43. 'allowNull': true
  44. },
  45. 'averagePrice': {//均价
  46. 'type': Sequelize.DOUBLE,
  47. 'allowNull': true
  48. },
  49. 'price': {//金额
  50. 'type': Sequelize.DOUBLE,
  51. 'allowNull': true
  52. },
  53. 'shareHoldingNum': {//变动后持股数目
  54. 'type': Sequelize.DOUBLE,
  55. 'allowNull': true
  56. },
  57. 'changeRatio': {//变动人变动比
  58. 'type': Sequelize.DOUBLE,
  59. 'allowNull': true
  60. },
  61. 'circulationCapitalRatio': {//占流通股本比例
  62. 'type': Sequelize.DOUBLE,
  63. 'allowNull': true
  64. },
  65. 'changeWay': {//变动方式
  66. 'type': Sequelize.STRING,
  67. 'allowNull': true
  68. },
  69. 'changePeople': {//股份变动人
  70. 'type': Sequelize.STRING,
  71. 'allowNull': true
  72. },
  73. 'changePeopleTitle': {//相关董事高管
  74. 'type': Sequelize.STRING,
  75. 'allowNull': true
  76. },
  77. 'duties': {//职务
  78. 'type': Sequelize.STRING,
  79. 'allowNull': true
  80. },
  81. 'relation': {//关系
  82. 'type': Sequelize.STRING,
  83. 'allowNull': true
  84. },
  85. 'industry': {//行业
  86. 'type': Sequelize.STRING,
  87. 'allowNull': true
  88. },
  89. }
  90. )
  91.  
  92. Ggzjc.sync({force: true}).then(function () {
  93. var url_array = get_url_array(1, 58)
  94. for (var i = 0; i < url_array.length; i++) {
  95. var data = get_data_from_url(url_array[i])
  96. sleep.usleep(200000)
  97. print(i + 1)
  98. print('complete!')
  99. for (var j = 0; j < data.length; j++) {
  100. var changeDate = '20' + data[j].changeDate
  101. var noticeDate = '20' + data[j].noticeDate
  102. var str_array = data[j].stockName.split("(")
  103. var stock_name = str_array[0]
  104. str_array = str_array[1].split(")")
  105. var stock_code = str_array[0]
  106. var changeNum = get_content_from_html(data[j].changeNum)
  107. if (changeNum != null) {
  108. if (changeNum == "")
  109. changeNum = null
  110. else
  111. changeNum = parseFloat(changeNum)
  112. }
  113. var averagePrice = data[j].averagePrice
  114. if (averagePrice == '&nbsp;')
  115. averagePrice = null
  116. else
  117. averagePrice = parseFloat(averagePrice)
  118. var price = get_content_from_html(data[j].price)
  119. if (price != null) {
  120. if (price == '')
  121. price = null
  122. else
  123. price = parseFloat(price)
  124. }
  125. var shareHoldingNum = data[j].shareHoldingNum
  126. if (shareHoldingNum == '&nbsp;')
  127. shareHoldingNum = null
  128. else
  129. shareHoldingNum = parseFloat(shareHoldingNum)
  130. var changeRatio = data[j].changeRatio
  131. if (changeRatio == '&nbsp;')
  132. changeRatio = null
  133. else
  134. changeRatio = parseFloat(changeRatio)
  135. var circulationCapitalRatio = data[j].circulationCapitalRatio
  136. if (circulationCapitalRatio == '&nbsp;')
  137. circulationCapitalRatio = null
  138. else
  139. circulationCapitalRatio = parseFloat(circulationCapitalRatio)
  140. var changeWay = data[j].changeWay
  141. if (changeWay == '&nbsp;')
  142. changeWay = null
  143. var changePeople = data[j].changePeople
  144. // console.log(data[j].changePeople)
  145. if (changePeople == '&nbsp;')
  146. changePeople = null
  147. var changePeopleTitle = data[j].changePeopleTitle
  148. if (changePeopleTitle == '&nbsp;')
  149. changePeopleTitle = null
  150. var duties = get_content_from_html(data[j].duties)
  151. if (duties != null && duties == '')
  152. duties = null
  153. var relation = get_content_from_html(data[j].relation)
  154. if (relation != null && relation == '')
  155. relation = null
  156. var industry = data[j].industry
  157. if (industry == '&nbsp;')
  158. industry = null
  159. var one = Ggzjc.build({
  160. 'stock_code': stock_code,
  161. 'stock_name': stock_name,
  162. 'changeDate': changeDate,
  163. 'noticeDate': noticeDate,
  164. 'changeNum': changeNum,
  165. 'averagePrice': averagePrice,
  166. 'price': price,
  167. 'shareHoldingNum': shareHoldingNum,
  168. 'changeRatio': changeRatio,
  169. 'circulationCapitalRatio': circulationCapitalRatio,
  170. 'changeWay': changeWay,
  171. 'changePeople': changePeople,
  172. 'changePeopleTitle': changePeopleTitle,
  173. 'duties': duties,
  174. 'relation': relation,
  175. 'industry': industry
  176. })
  177. one.save()
  178. }
  179.  
  180. }
  181.  
  182. })
  183. }

  需要解析并获取html标签<tag>content</tag>中的content,利用正则表达式取出><中间的文本就可以了.

  1. function get_content_from_html(str) {
  2. var pattern = />[\s\S]+?</g
  3. var res = str.match(pattern)
  4. if (res == null) {
  5. return null
  6. }
  7. var result = res[0]
  8. return result.slice(1, result.length - 1)
  9. }

done!

附注:

  借助python execjs和pandas,我实现了以更加优美的姿势爬取上述内容,代码详见我的github:

  https://github.com/zhoudayang/get_hexun

使用nodejs爬取和讯网高管增减持数据的更多相关文章

  1. 网络爬虫之定向爬虫:爬取当当网2015年图书销售排行榜信息(Crawler)

    做了个爬虫,爬取当当网--2015年图书销售排行榜 TOP500 爬取的基本思想是:通过浏览网页,列出你所想要获取的信息,然后通过浏览网页的源码和检查(这里用的是chrome)来获相关信息的节点,最后 ...

  2. 使用python爬取东方财富网机构调研数据

    最近有一个需求,需要爬取东方财富网的机构调研数据.数据所在的网页地址为: 机构调研 网页如下所示: 可见数据共有8464页,此处不能直接使用scrapy爬虫进行爬取,因为点击下一页时,浏览器只是发起了 ...

  3. Node.js爬虫-爬取慕课网课程信息

    第一次学习Node.js爬虫,所以这时一个简单的爬虫,Node.js的好处就是可以并发的执行 这个爬虫主要就是获取慕课网的课程信息,并把获得的信息存储到一个文件中,其中要用到cheerio库,它可以让 ...

  4. python 爬虫之爬取大街网(思路)

    由于需要,本人需要对大街网招聘信息进行分析,故写了个爬虫进行爬取.这里我将记录一下,本人爬取大街网的思路. 附:爬取得数据仅供自己分析所用,并未用作其它用途. 附:本篇适合有一定 爬虫基础 crawl ...

  5. Python爬虫之爬取慕课网课程评分

    BS是什么? BeautifulSoup是一个基于标签的文本解析工具.可以根据标签提取想要的内容,很适合处理html和xml这类语言文本.如果你希望了解更多关于BS的介绍和用法,请看Beautiful ...

  6. 基于爬取百合网的数据,用matplotlib生成图表

    爬取百合网的数据链接:http://www.cnblogs.com/YuWeiXiF/p/8439552.html 总共爬了22779条数据.第一次接触matplotlib库,以下代码参考了matpl ...

  7. 八爪鱼采集器︱爬取外网数据(twitter、facebook)

    每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 要想采集海外数据有两种方式:云采集+单机采集. ...

  8. 爬虫入门(四)——Scrapy框架入门:使用Scrapy框架爬取全书网小说数据

    为了入门scrapy框架,昨天写了一个爬取静态小说网站的小程序 下面我们尝试爬取全书网中网游动漫类小说的书籍信息. 一.准备阶段 明确一下爬虫页面分析的思路: 对于书籍列表页:我们需要知道打开单本书籍 ...

  9. Scrapy爬虫(5)爬取当当网图书畅销榜

      本次将会使用Scrapy来爬取当当网的图书畅销榜,其网页截图如下:   我们的爬虫将会把每本书的排名,书名,作者,出版社,价格以及评论数爬取出来,并保存为csv格式的文件.项目的具体创建就不再多讲 ...

随机推荐

  1. PHPstorm端口配置问题

  2. usb调试

    修改文件:/home/mxy/code/v1/kernel-3.10/drivers/power/mediatek/battery_common.c //bool AutoDebug=true;//x ...

  3. windows 2003添加删除windows组件中无iis应用程序服务器项的解决方法

    解决方法如下: 1.开始 -- 运行,输入 c:\Windows\inf\sysoc.inf,会打开这个文件;在sysoc.inf中找到"[Components]"这一段,并继续找 ...

  4. sql server 行转列 Pivot UnPivot

    SQL Server中行列转换 Pivot UnPivot 本文转自:张志涛 原文地址: http://www.cnblogs.com/zhangzt/archive/2010/07/29/17878 ...

  5. PHP模板解析类实例

    作者:mckee 这篇文章主要介绍了PHP模板解析类,涉及php针对模板文件的解析与字符串处理的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下 <?php class template { ...

  6. 百度网盘API的操作--PCS 百度个人云存储 上传 ,下载文件

    来自http://blog.csdn.net/u014492257/article/details/39856403 另外需要所有API使用方法的请访问本人上传的资源(需要3个下载分的)链接: htt ...

  7. [Unity]背包效果-使用NGUI实现物品的拖拽效果Drag

    背包效果-使用NGUI实现物品的拖拽效果Drag 效果实现如图 对象层级关系图 PacketCell - Right 对象作为单元格背景 PacketContainer 对象作为单元格容器 Packe ...

  8. Query插件之ajaxFileUpload使用方法——input.change()事件的时候实现文件上传

    点击下载 这是HTML <input id="uploadedfile" name="uploadedfile" type="file" ...

  9. CodeForces 605B Lazy Student

    构造.对边的权值排序,权值一样的话,在MST中的边排到前面,否则权值小的排在前面. 然后边一条一条扫过去,如果是1 ,那么连一个点到集合中,如果是0,集合内的边相连. #include<cstd ...

  10. 左倾堆(C#)

    参考:http://www.cnblogs.com/skywang12345/p/3638384.html using System; using System.Collections.Generic ...