帮忙在金融行业的同学爬了个虫...

今天突然有同学来问她目前遇到的棘手问题,爬虫代码出问题了

求助图片

我初看了一下代码。

import urllib
import urllib2
import re
url = 'http://data.eastmoney.com/kzz/detail/113021.html'
requst = urllib2.Request(url)
reponse = urllib2.urlopen(requst)
data = reponse.read()
data = data.split("\r\n")
data = " ".join(data)
linshi = re.findall("可转债详细资料.*?东方财富Choice数据",data)
print linshi

她主要是想爬取东方财富的页面数据,来帮她节约整理文档的时间。想通过之前我教她的截取所需要的文本块来定位到需要的数据,过滤掉脏文本。

但代码出问题了,输出结果为空!

我试着运行了她发给我的代码,发现并没有代码上的bug。于是试着打印了页面的数据,发现中文出现了乱码。

<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=654">ָ��</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=655">��ָ</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=3646">��Ȩ</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=656">����</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=657">���</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=658">����</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=660">�¹�</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=661">����</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=663">�۹�</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=665">����</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=666">�ڻ�</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=667">���</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=668">�ƽ�</a>|<a target="_blank" href="http://favor.fund.eastmoney.com/">��ѡ��</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=670">��ѡ����</a>

定位到了,是编码问题!这对于python的新手来说是一个棘手的问题,经常会导致他们无法继续进行下去。

我尝试对data直接进行编码的转化。

data = data.decode('utf-8')

运行完会提示以下的错误

UnicodeDecodeError: 'utf8' codec can't decode byte 0xbf in position 146: invalid start byte

转化编码失败,猜测大概的原因是没确定好网页的原始编码,最终使用了以下语句,成功进行了编码转化。

data = data.decode('gb2312','ignore')

转化后的网页打印结果,转化成功

<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=654">指数</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=655">期指</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=3646">期权</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=656">个股</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=657">板块</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=658">排行</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=660">新股</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=661">基金</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=663">港股</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=665">美股</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=666">期货</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=667">外汇</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=668">黄金</a>|<a target="_blank" href="http://favor.fund.eastmoney.com/">自选股</a>|<a target="_blank" href="http://js1.eastmoney.com/tg.aspx?ID=670">自选基金</a> 

编码问题解决了,我们就可以顺利地进行文本定位了。看了一下表格部分的代码

         <table cellpadding="0" cellspacing="0" class="tab1" id="tab1" style="width:1000px;table-layout:fixed;">
                <tbody>
                        <tr>
                            <td rowspan="10" style="width:130px;" class="td_1">发行状况</td>
                            <td class="bg" style="width:22%;">债券代码</td>
                            <td style="width:20%;" class="zqcode">-</td>
                            <td class="bg">债券简称</td>
                            <td style="width:20%;" class="zqname">-</td>
                        </tr>
                        ...
                    
                        <tr>
                            <td class="bg">上市日期</td>
                            <td class="listdate">-</td>
                            <td class="bg">网上发行中签率(%)</td>
                            <td class="lucyrate">-</td>
                        </tr>
                    </tbody>                    
                </table>

发现数据部分并没有直接打印在网页上,可以断定东方财富已经进行了技术更新,采用了现在比较流行的“前后端分离”。所以我们要去拦截网页的数据交互,定位到对应的数据接口。由于东方财富是开放的网站,不需要用户登录,所以我们就省了爬虫中常用的“模拟登录”。这样一切就变得更简单了。

如何拦截数据接口?
我之前一直在用FireFox的一个插件,叫HttpFox,是一个相当好用的拦截数据的工具。但由于Firefox更新新版后,刻意回避一些会导致安全问题的插件,所以HttpFox就基本用不了了。
退而求其次,就直接使用Chrome的开发者工具吧,虽然界面有点繁复,但也挺好用的。

最终的实现代码

# -*- coding: utf-8 -*-  

import urllib
import urllib2
import re
import json
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
url='http://data.eastmoney.com/kzz/detail/113021.html'

result_dict = {}
#第一步,通过url直接获取申购代码
code = re.findall("\d+",url)
result_dict["申购代码"] = code[-1]

#第二步,找到对应的接口,获取所需数据进行存储
reponse = urllib2.urlopen("http://dcfm.eastmoney.com/em_mutisvcexpandinterface/api/js/get?type=KZZ_LB&token=70f12f2f4f091e459a279469fe49eca5&filter=(BONDCODE=%27" + result_dict["申购代码"] + "%27)&js=var%20KZZ_LB=[(x)]")
Jdata1 = reponse.read()
Jdata1 = re.findall("{.*?}",Jdata1)
Jdata1 = json.loads(Jdata1[0])
result_dict["债券简称"] = Jdata1["SNAME"]
result_dict["正股价"] = Jdata1["ZGJ"]
result_dict["转股价"] = Jdata1["ZGJZGJ"]
result_dict["转股价值"] = Jdata1["ZGJZGJJZ"]

reponse = urllib2.urlopen("http://dcfm.eastmoney.com/em_mutisvcexpandinterface/api/js/get?type=KZZ_MX&token=70f12f2f4f091e459a279469fe49eca5&filter=(BONDCODE=%27" + result_dict["申购代码"] + "%27)&js=var%20KZZ_MX=[(x)]")
Jdata1 = reponse.read()
Jdata1 = re.findall("{.*?}",Jdata1)
Jdata1 = json.loads(Jdata1[0])
result_dict["信用评级"] = Jdata1["CREDITRATING"]

#第三步,组装输出语句
out = "今日可申购可转债"
out += result_dict["债券简称"] + ",申购代码"
out += result_dict["申购代码"] + "。正股价"
out += str(result_dict["正股价"]) + "元,转股价"
out += str(result_dict["转股价"]) + ",转股价值"
out += str(round(result_dict["转股价值"],2)) + "。信用评级"
out += result_dict["信用评级"] + "。"

print out
comments powered by Disqus