:trollface: 爬虫Demo,基于Python实现
网络爬虫
也叫网络蜘蛛
,如果把互联网比喻成一个蜘蛛网,那么蜘蛛就是在网上爬来爬去的蜘蛛,爬虫程序通过请求url地址,根据响应的内容进行解析采集数据,
比如:如果响应内容是html,分析dom结构,进行dom解析、或者正则匹配,如果响应内容是xml/json数据,就可以转数据对象,然后对数据进行解析。
通过有效的爬虫手段批量采集数据,可以降低人工成本,提高有效数据量,给予运营/销售的数据支撑,加快产品发展。
目前互联网产品竞争激烈,业界大部分都会使用爬虫技术对竞品产品的数据进行挖掘、采集、大数据分析,这是必备手段,并且很多公司都设立了爬虫工程师
的岗位
爬虫是利用程序进行批量爬取网页上的公开信息,也就是前端显示的数据信息。因为信息是完全公开的,所以是合法的。其实就像浏览器一样,浏览器解析响应内容并渲染为页面,而爬虫解析响应内容采集想要的数据进行存储。
爬虫很难完全的制止,道高一尺魔高一丈,这是一场没有硝烟的战争,码农VS码农
反爬虫一些手段:
urllib
urllib2
cookielib
threading
re
json
pyquery
beautiful soup
selenium
斗鱼主播排行
申明:此例子仅作为爬虫学习DEMO,并无其他利用
基于python实现爬虫学习基础demo
def douyu_rank(rankName, statType):
'''
斗鱼主播排行数据抓取
[数据地址](https://www.douyu.com/directory/rank_list/game)
* `rankName` anchor(巨星主播榜),fans(主播粉丝榜),haoyou(土豪实力榜),user(主播壕友榜)
* `statType` day(日),week(周),month(月)
'''
if not isinstance(rankName, ERankName):
raise Exception("rankName 类型错误,必须是ERankName枚举")
if not isinstance(statType, EStatType):
raise Exception("statType 类型错误,必须是EStatType枚举")
rankName = '%sListData' % rankName.name
statType = '%sListData' % statType.name
# 请求获取html源码
rs = rq.get(
"https://www.douyu.com/directory/rank_list/game",
headers={'User-Agent': 'Mozilla/5.0'})
# 正则解析出数据
mt = re.search(r'rankListData\s+?=(.*?);', rs, re.S)
if (not mt):
print u"无法解析rankListData数据"
return
grps = mt.groups()
# 数据转json
rankListDataStr = grps[0]
rankListData = json.loads(rankListDataStr)
dayList = rankListData[rankName][statType]
# 修改排序
dayList.sort(key=lambda k: (k.get('id', 0)), reverse=False)
return dayList
def douyu_room(romm_id):
'''
主播房间信息解析
[数据地址](https://www.douyu.com/xxx)
'romm_id' 主播房号
'''
rs = rq.get(
("https://www.douyu.com/%s" % romm_id),
headers={'User-Agent': 'Mozilla/5.0'})
mt = re.search(r'\$ROOM\s+?=\s+?({.*?});', rs, re.S)
if (not mt):
print u"无法解析ROOM数据"
return
grps = mt.groups()
roomDataStr = grps[0]
roomData = json.loads(roomDataStr)
return roomData
def run():
'''
测试爬虫
'''
datas = douyu_rank(ERankName.anchor, EStatType.month)
print '\r\n主播排行榜:'
for item in datas:
room_id = item['room_id']
roomData = douyu_room(room_id)
rommName = None
if roomData is not None:
rommName = roomData['room_name']
roomInfo = (u'房间(%s):%s' % (item['room_id'], rommName))
print item['id'], item[
'nickname'], roomInfo, '[' + item['catagory'] + ']'
run()
运行结果:
主播排行榜:
无法解析ROOM数据
1 冯提莫 房间(71017):None [英雄联盟]
2 阿冷aleng丶 房间(2371789):又是我最喜欢的阿冷ktv时间~ [英雄联盟]
3 胜哥002 房间(414818):胜哥:南通的雨下的我好心累。 [DNF]
4 White55开解说 房间(138286):卢本伟五五开 每天都要很强 [英雄联盟]
5 东北大鹌鹑 房间(96291):东北大鹌鹑 宇宙第一寒冰 相声艺术家! [英雄联盟]
6 老实敦厚的笑笑 房间(154537):德云色 给兄弟们赔个不是 [英雄联盟]
7 刘飞儿faye 房间(265438):刘飞儿 月底吃鸡 大吉大利 [绝地求生]
8 pigff 房间(24422):【PIGFF】借基地直播,没OW [守望先锋]
9 云彩上的翅膀 房间(28101):翅:还是抽天空套刺激! [DNF]
10 yyfyyf 房间(58428):无尽的9月,杀 [DOTA2]
# 冯提莫 房间做周年主题,解析会有问题
图1-意淫爬虫与反爬虫间的对决
如今已然是大数据时代,数据正在驱动着业务开发,驱动着运营手段,有了数据的支撑可以对用户进行用户画像,个性化定制,数据可以指明方案设计和决策优化方向,所以互联网产品的开发都是离不开对数据的收集和分析,数据收集的一种是方式是通过上报API进行自身平台用户交互情况的捕获,还有一种手段是通过开发爬虫程序,爬取竞品平台的数据,后面就重点说下爬虫的应用场景和实践中会遇到的问题和反反爬虫的一些套路与技巧。
做爬虫开发,需要对WEB这块有相对全面深入的理解,这样后面遇到反爬虫才能得心应手,见招拆招
反爬虫可以分为
服务端限制
和前端限制
服务端限制
:服务器端行请求限制,防止爬虫进行数据请求
前端限制
:前端通过CSS和HTML标签进行干扰混淆关键数据,防止爬虫轻易获取数据
设置请求头(服务端限制
)
签名规则(服务端限制
)
延迟,或者随机延迟(服务端限制
)
代理IP(服务端限制
)
登录限制(服务端限制
)
验证码限制(服务端限制
)
CSS/HTML混淆干扰限制(前端限制
)
前端通过CSS或者HTML标签进行干扰混淆关键数据,破解需要抽样分析,找到规则,然后替换成正确的数据
1 . font-face,自定义字体干扰
<!--css-->
<!--找到://k3.autoimg.cn/g13/M05/D3/23/wKjByloAOg6AXB-hAADOwImCtp047..ttf-->
<style>
@font-face {font-family: 'myfont';src: url('//k2.autoimg.cn/g13/M08/D5/DD/wKgH41oAOg6AMyIvAADPhhJcHCg43..eot');src: url('//k3.autoimg.cn/g13/M08/D5/DD/wKgH41oAOg6AMyIvAADPhhJcHCg43..eot?#iefix') format('embedded-opentype'),url('//k3.autoimg.cn/g13/M05/D3/23/wKjByloAOg6AXB-hAADOwImCtp047..ttf') format('woff');}
</style>
<!--html-->
<!--会员招募中-->
<div> Mercedes C+ 会员招募<span style='font-family: myfont;'></span></div>
<!--
从html中获取【html中文编码】=
然后解析ttf文件得到【ttf中文编码】列表
匹配发现【ttf中文编码】=uniF159可以与【html中文编码】=匹配,在第7个,第7个中文就是"中"
(抽样分析会发现ttf中中文位置是固定的,中文编码是动态变化的,所以只要映射出【ttf中文编码】索引就可以知道中文字符了)
-->
破解思路:
找到ttf字体文件地址,然后下载下来,使用font解析模块包对ttf文件进行解析,可以解析出一个字体编码的集合,与dom里的文字编码进行映射,然后根据编码在ttf里的序号进行映射出中文
可以使用FontForge/FontCreator工具打开ttf文件进行分析
2 . 伪元素隐藏式
通过伪元素来显示重要数据内容
如例子:汽车X家
<!--css-->
<style>
.hs_kw60_configod::before {
content: "一汽";
}
.hs_kw23_configod::before {
content: "大众";
}
.hs_kw26_configod::before {
content: "奥迪";
}
</style>
<!--html-->
<div>
<span class="hs_kw60_configod"></span>
-
<span class="hs_kw23_configod"></span>
<span class="hs_kw26_configod"></span>
</div>
破解思路:
找到样式文件,然后根据HTML标签里class名称,匹配出CSS里对应class中content的内容进行替换
3 . backgroud-image
通过背景图片的position位置偏移量,显示数字/符号,如:价格,评分等
根据backgroud-postion值和图片数字进行映射
4 . html标签干扰
通过在重要数据的标签里加入一些有的没的隐藏内容的标签,干扰数据的获取
如例子:xxIP代理平台
<!--html-->
<td class="ip">
<p style="display:none;">2</p>
<span>2</span>
<span style="display:inline-block;"></span>
<div style="display: inline-block;">02</div>
<p style="display:none;">.1</p>
<span>.1</span>
<div style="display:inline-block;"></div>
<span style="display:inline-block;"></span>
<div style="display:inline-block;">09</div>
<span style="display: inline-block;">.</span>
<span style="display:inline-block;">23</span>
<p style="display:none;">7</p>
<span>7</span>
<p style="display:none;"></p>
<span></span>
<span style="display: inline-block;">.</span>
<div style="display: inline-block;"></div>
<p style="display:none;">3</p>
<span>3</span>
<div style="display: inline-block;">5</div>:
<span class="port GEA">80</span>
</td>
<!--js-->
<script>
$(".ip:eq(0)>*:hidden").remove()
$(".ip:eq(0)").text()
</script>
<!--
输出:202.109.237.35:80
通过移除干扰标签里有display:none隐藏标签,然后再获取text就不会有干扰的内容了
-->
破解思路:
过滤掉干扰混淆的HTML标签,或者只读取有效数据的HTML标签的内容
... ... (反爬虫脑洞有多大,反反爬虫拆招思路就有多淫荡)
防止投毒
font解析 C#和Python实现
/// 需要引入PresentationCore.dll
private void Test() {
string path = @"F:\font.ttf";
//读取字体文件
PrivateFontCollection pfc = new PrivateFontCollection();
pfc.AddFontFile(path);
//实例化字体
Font f = new Font(pfc.Families[0], 16);
//设置字体
txt_mw.Font = f;
//遍历输出
var families = Fonts.GetFontFamilies(path);
foreach (System.Windows.Media.FontFamily family in families) {
var typefaces = family.GetTypefaces();
foreach (Typeface typeface in typefaces) {
GlyphTypeface glyph;
typeface.TryGetGlyphTypeface(out glyph);
IDictionary<int, ushort> characterMap = glyph.CharacterToGlyphMap;
var datas = characterMap.OrderBy(d => d.Value).ToList();
foreach (KeyValuePair<int, ushort> kvp in datas) {
var str = $"[{kvp.Value}][{kvp.Key}][{(char)kvp.Key}]\r\n";
txt_mw.AppendText(str);
}
}
}
}
# pip install TTFont
from fontTools.ttLib import TTFont
from fontTools.merge import *
me = Merger()
font = TTFont('./font.ttf')
cmaps = font.getBestCmap()
orders = font.getGlyphOrder()
# font.saveXML('F:/1.xml')
print cmaps
print orders