4月14日,乐视董事长贾跃亭高调发布乐视手机,然而比手机更抢眼的是他标志性的着装:一件在无数场合都未曾变过的黑色T恤。在科技圈,如同贾跃亭般穿出个人风格的大佬不止他一个,今天为大家盘点科技圈大佬们独特的穿衣品味。
【马云:彩虹毛衣换色穿】阿里巴巴创始人马云似乎偏爱彩色毛衣,在其参加的历次行业大会上,这位大佬不是红毛衣就是黄毛衣,绿、橙、蓝、灰毛衣也是来者不拒。2014年3月,马云就因与法国外交部长合影时身披西装内穿毛衣,而被众人诟病装不得体。2014年双11庆功宴上,他又穿起了大红毛衣表示庆祝。而在行业活动、演讲等等时候毛衣更是成为了马云的标配。
【乔布斯:经典的黑上衣+牛仔裤】前苹果CEO乔布斯非常喜欢黑色上衣加蓝色牛仔裤这种套装形式,以至于在纪念他生平的电影《乔布斯传》中,拍摄共用了60条李维斯的牛仔裤。据为其写传记的人披露,乔布斯最初是希望这种着装成为公司制服,虽然后来此事不了了之,但他却一直坚持了这种穿衣风格。
【扎克伯格:最爱随性的连帽衫】作为全球最年轻的自行创业亿万富豪,扎克伯格始终保持阳光大男孩的年轻形象,这与他爱穿连帽衫也是分不开的。无论是与爱妻散步,接受采访甚至连Facebook IPO路演时他都是一件连帽衫。不也因为小扎的这种随性,给他带来了不少麻烦。路演成功结束后,他却被华尔街分析师批评不成熟。
【史玉柱:一身红衣 造型妈妈定】巨人公司董事史玉柱总是一身红衣,在公共视线里他总是红红火火,无论哪一次访谈或微访谈都是如此,就连他的自述封面也不放过。曾有记者问他为什么爱穿红衣服,他表示是妈妈买的,造型是由妈妈决定。
【盖茨:带领衬衫+V领毛衣】作为曾经的全球首富,比尔盖茨的衣服搭配显得平民很多,一件带领衬衫外搭V领毛衣经常出现,无论是发布新品还是在XP版本更新会上。
【雷军:纯黑色T恤 +水洗牛仔裤 + 帆布鞋】小米创始人雷军十分崇拜乔布斯,连穿衣风格都在向偶像致敬,几次小米发布会上,雷军一律没有logo的纯黑T恤与牛仔裤搭配,脚踩帆布鞋。想来下次小米再发新品,雷军的行装也会一如既往。
【罗永浩:永远的TOMMY HILFIGER 藏蓝色衬衫】锤子科技创始人罗永浩似乎对衣服十分偏执,一直以来无论正式与非正式场合,他都执着于藏蓝色系扣衬衫,而且只是TOMMY HILFIGER这个牌子。一件经典的藏蓝色衬衫春夏不变:天热穿一件,天冷了就在外面加个外套继续穿。
1,T恤,简单大方,而且不会出错配什么都ok,最主要的是,我懒呀,哈哈哈哈,这个世界太复杂了,衣服都有那么多种,这种事情还是未来女朋友来教我吧.
2,纯色,还是简单大方,干净利落,我的衣服基本都是白色或者灰色的纯色,不会让别人第一眼就看着不舒服,而且也是懒呀,哈哈哈哈
3,科技界讲究的就是至繁归于至简,有什么能比一件纯色的t恤更能体现这种感觉吗?
4,理工科男生真的懒….我们能做的只能做到干干净净呀,剩下什么的,等着被拯救吧,哈哈哈哈
1,为什么穿T恤?
简约风格是当前科技行业的时尚。事实上其他传统行业的高管级领导,一般穿着的西装,也是传统而内敛的。夸张的配色和张扬的造型,是娱乐圈的选择。科技行业在今天更愿意用单纯的技术、产品来说话,而不把这些形而上的东西当成某种政治正确的必须。
无论是扎克伯格,还是雷军,还是乔布斯,在日常工作中也是保持了这样的穿衣风格,他们并非刻意打造,只是日常就不愿意太过于把精力放在挑什么颜色的领带这种事情上。
2,为什么是纯色?
至少对于雷总来说,就是单纯的因为它简单,低调。大家做事情的,做产品的,没必要把自己搞得那么夸张。雷总不止一次被公众抓拍到他自己去打车,去乘坐飞机坐经济舱等,可以看到他日常就是那样的风格。
扎克伯格在办公室,在家,在路上,也都是这种简约的风格。
3,为什么在发布会上都是纯色的?
除了之前两点原因之外,纯色服装,对于发布会上的视觉效果很有帮助。为了做好一场发布会,需要准备的也不仅仅是Keynote的内容,动画,视频等东西,包括灯光,音响等效果也是必要考虑的。小米在这个领域里也是从什么都不懂,一点点摸索出来的,每一次发布会都能发现很多不足,然后下一次改进。这其中就包括雷军的服装选择。
4,如果你真的留意过,你会看到,雷军在近两年的所有发布会上,并没有穿T恤,而是一件淡淡的蓝色的衬衫,牛仔裤和帆布鞋。都是凡客牌的。
先说一下乔布斯吧,他的算是有型有款了,是三宅一生设计的,然后他一次性存了好几十件,所以一直穿着看着一样的长袖。据说这样穿是因为乔布斯喜欢极简,比较讨厌多余的按键(英语里按键和纽扣都叫BUTTONS/ No Buttons)。
扎克伯格一直都是那样穿的,平时可能更随意一点,所以发布会这样也没什么稀奇。
雷军,实在不知道,平时好像穿的是西装的,不知道怎么到发布会风格就变了。继续发扬八卦精神可能是因为他外号雷布斯吧
谈到雷布斯,倒想起另外一个手机业界人物,罗永浩。最出名的一句应该是:我一个人来到了科技与人文的十字路口,只看到老乔的墓碑孤零零地立在那。这位号称继承乔布斯衣钵的,穿衣服倒是弄出了另外一种风格,一直都穿着件深色的Tommy Hilfiger衬衫
与其说是乔布斯把皮克斯卖给了迪士尼,不如说是迪士尼一定要把皮克斯买过来。
在2006年收购发生之前,迪士尼和皮克斯已经持续了十多年的合作关系,它们是基于投资、利润均五五对分的方式合作,皮克斯负责制片,迪士尼负责发行。因为在合作的开始,迪士尼地位更加强势,所以合作方式的细节是对迪士尼更加有利的,比如皮克斯照样要掏发行费用给迪士尼,迪士尼拥有故事的版权和续集权,导致皮克斯在很多方面受制于迪士尼的决定,这为后来的冲突埋下伏笔。在这段合作关系行将结束的时候,迪士尼本身的动画创作已经非常疲软,相反,皮克斯成为好莱坞最赚钱的公司,话语权强弱互易,在重新开启新的合作谈判的时候,乔布斯和当时迪士尼的CEO Michael Eisner发生了激烈冲突。皮克斯希望同迪士尼仅仅建立一种简单的发行代理关系,那么投资、利润都将由皮克斯独立承担。换句话说,皮克斯翅膀硬了,要更大的控制权。如果迪士尼不同意,那么皮克斯将选择其他发行公司。
这期间发生了很多事情,Eisner离开了占据近30年的迪士尼CEO位置,取而代之的是Robert Iger。Iger上任之后,反复重申,动画片的生产,仍然是迪士尼公司的核心业务,所以他决心改善和皮克斯与乔布斯的关系。
从迪士尼的角度来说,它需要皮克斯胜过皮克斯需要迪士尼。因为好莱坞动画时下的领头羊是皮克斯和梦工厂,迪士尼早已风光不再,它迫切需要内容。这种渴望极其强烈,以至于迪士尼觉得,倘若不能和皮克斯达成相比老协议更加有利的新合作方式,那何不干脆将其收购?所以最后迪士尼给出了74亿美元的换股条件。
从乔布斯和皮克斯股东的角度来说,首先这个价格非常实惠,几乎所有人都觉得迪士尼给高了,那么有什么理由不卖?换股收购协议让拥有皮克斯超过50%股权的乔布斯成为迪士尼公司最大的个人股东(大约7%),超过了原CEO Michael Eisner,及迪士尼元老、Walter Disney的侄子Roy E. Disney,并取得迪士尼公司董事局的席位。同时John Lasseter等原皮克斯高层及创意核心都在新的迪士尼-皮克斯动画生产构架中得到了不错的位置。并且为了保证皮克斯企业文化的独立性,迪士尼并未对其动画制作方式和人力资源施加不必要的影响。那么,皮克斯基本上还是原来的皮克斯。另外对乔布斯来说,成为迪士尼的大股东,也有利于他在传媒娱乐领域中发挥更大的作用,迪士尼是一个综合性的媒体帝国,拥有数不清的业务分支,乔布斯藉此收获的影响力只会扩大,不会缩小。获得娱乐内容生产的控制权,对苹果公司无疑也有着不可低估的积极意义。
从好莱坞公司发展壮大的自身逻辑来说,皮克斯在一定程度上也需要迪士尼。自80年代以来,好莱坞已经形成了六大跨国传媒集团垄断的超稳定结构,这几乎是不可动摇的。这些大集团不仅垂直垄断了电影制作的各个环节(其垄断程度已经超过50年代以前的大片厂时期),更横跨其他所有媒体部门。不论多么雄心勃勃的独立制片公司,如80年代的Orion Pictures,90年代的梦工厂、米拉麦克斯,抑或靠《指环王》崛起的新线,都只能轮流风光三五年,这些公司无一不想成为综合性的娱乐集团,但都没法办到。皮克斯也办不到。作为单一的制片公司,皮克斯的优秀程度已经逼近了极限,要想更进一步发展,它必须在垂直和水平方向和其他环节整合起来,而这是不可能自动从现有的皮克斯内部生长出来的,那么并入某个集团公司是最好的捷径。
所以,迪士尼收购皮克斯,其实是顺理成章的一件事,在正常情况下,也应该是双赢的。
某日,打开《什么值得买》,
这是一个神奇的网站。
偶然间,看到维多利亚的秘密、真人秀,等关键词,觉得貌似可以做点什么。
于是,学下Python,撸了个单线程爬虫(很慢- -)。
从撸好到现在,爬了:
其中,含“真人”关键词的,有:
我们按照点赞数、评论数排个序:
然后,点开邪恶的 images 字段:
嗯。。可以做点什么了。
等数据全部爬完了,准备写个前端页面,方便浏览。
另,用http://socket.io写了个简陋的日志监控:
综上,要开始能写爬虫,需要一个动手的动机,因人而异。。
第一次想写爬虫是为了抓取草榴的高点击率视频链接。代码如下。需要翻墙。
# — coding: utf-8 —
import urllib2
import sys
from bs4 import BeautifulSoup
reload(sys)
sys.setdefaultencoding(‘utf8′) #解决写入文件乱码问题
BaseUrl = "http://t66y.com/"
j=1
for i in range(1, 100): #设置始末页码
url = "http://t66y.com/thread0806.php?fid=22&search=&page="+ str(i) #默认str会把字符串变成unicode,所以开头必须用sys来重置
page = urllib2.urlopen(url)
soup = BeautifulSoup(page, from_encoding="gb18030") #解决BeautifulSoup中文乱码问题
print("reading page "+ str(i))
counts = soup.find_all("td", class_="tal f10 y-style")
for count in counts:
if int(count.string)>15: #选择想要的点击率
videoContainer = count.previous_sibling.previous_sibling.previous_sibling.previous_sibling
video = videoContainer.find("h3")
print("Downloading link "+ str(j))
line1 = (video.get_text())
line2 = BaseUrl+video.a.get(‘href’)
line3 = "view **" + count.string + "** "
print line1
f = open(‘cao.md’, ‘a’)
f.write("n"+"###"+" "+line1+"n"+"<"+line2+">"+"n"+line3+ " "+ "page"+str(i)+"n")
f.close()
j+=1
步骤:1 登录 输入user和password
2 如果用户名和密码错误:
提示重新输入
否则:
开始扒图片,默认路径~/图片/pixiv/(当日日期)
3 下载完毕通知
【1】 可能遇到/需要解决:
1 下载到一半突然中断
2 pixiv自己可能出现的错误
3 让脚本自动打开原始大小的图片。
4 内存不足时提醒
5 不能下载重复的图片
【2】用哪种语言:python2.7
【3】需要掌握的知识:
1.python爬虫
Urllib库的使用,Cookie,Scrapt框架(ImagePipeline,Signal,内置中间件)
2.linux基础
3.正则表达式
4.一些基本的网页知识。
【4】希望以后可以实现搜索图片后,自动下载大于四星的作品。(用到正则表达式?)
【5】所以实现/学习的顺序(?)
1 实现脚本登录pixiv
2 实现下载固定页面的图片
3 实现下载日榜打开后的页面的图片(非缩略图,下同)
实现下载提示(下载了几张)
下载时新建今日日期的文件夹,并保存到其中
4 实现下载月榜打开后的页面图片
5 实现下载新秀后的页面的图片
6 根据输入的不同,下载的榜单有所变化
是否要使用Proxy呢?思考
学习资料:
教程:Python爬虫学习系列教程
搜索引擎:google
————————————————–
7.11
看教程学习urllib2库:Request,response,POST和GET(动态网页)
POST方式传送:
登录一个网站,设置一个字典values并用urlencode将字典编码名为data。
给request传入两个参数 url和data即可实现登录。返回的是登录后呈现的页面内容。
GET方式传送
写一个 Python 爬虫其实挺容易的,也许过程中需要不停的调试,但爬虫的代码绝对短小精悍!
肇事者爬虫
这就是我昨天把郑子涵五子棋网站搞挂的 Python 爬虫,文件名为 spider004.py。没错是我写的第四个爬虫…今天为了防止我继续恶意开房,他给网站加了验证码。目前我还无能为力= =
HTTP 协议
要写一个爬虫,首先你得懂 HTTP 协议的一些东西。我当时是看了这篇文章《HTTP协议详解》。我简单说下自己的理解:
当你用浏览器访问网页的时候,其实是经历了这么一系列的过程:你通过浏览器发出 HTTP 请求 (HTTP Request),发送请求有多种方法,常见的两种是 GET 和 POST。网页的服务器端收到你的请求进行处理后,就会给浏览器响应 (HTTP Response)。而 HTTP Request 和 HTTP Response 都是有参数哒,你可以通过 F12 来看。
这很好理解,因为交互各种各样,访问不同的网址,是否登录账号,用什么浏览器,甚至在不同的时间发起请求,都会导致 HTTP Request 的不同。而服务器端挂了,状态码(status_code) 可能是 502 Bad Gateway,又或者 404 Not Found,这都是平时常见的错误,就是 HTTP Response 的一部分。(你可能会把它统称为:“ 啊,网页怎么打不开了!”,其实打不开也是有不同的原因的)。
差不多先了解这些,之后写爬虫的过程中,我们还会常用 F12,利用 HTTP Request/Response 的信息来调试。
Python Requests Module
所谓的网页爬虫,就是模仿浏览器向服务器发送特定的请求,并且返回获取的内容。而 Python 为此提供了一个很好用的模块,Requests 模块。
我最早是看了这个教程《HTML Scraping》,并且按照上面操作了一遍。这个教程提到抓取网页内容可以用的两种定位方法 XPath 和 CSSSelect,虽然我都没学会..
然后还看了这两个教程,一个是《Requests: HTTP for Humans》官方文档,另一个是《Using Requests in Python》简单教程。
我们可以简单的演示一下(终端中打开python3):
>>> import requests
>>> page = requests.get("http://www.baidu.com"
>>> page.url
‘http://www.baidu.com/’
>>> page.status_code
200
>>> page.headers
(省略)
>>> page.text
(省略)
如果你需要的是抓取网站获取的信息(显示的HTML文件),可以把 page.text 部分输出到文件中。
把网站搞垮的具体细节
我观察到五子棋网站下棋开的房间是随机6位数,而且保存所有的棋盘信息,也就是你下完棋哪怕两个人都退出了以后,房间还是被占用的,你们还可以之后再进去继续玩。也就是,“如果我把所有的房间都开满了,那其他人没法开新的房间,也没法再已经被我开的房间里下棋了”←我对网站的攻击就是基于这个思路。
首先,自己打开浏览器里的匿名窗口操作一遍(匿名窗口是防止 cookie 混乱),并且按F12打开控制台, 观察到我新建房间时发出的请求获取的文件,如
戳进去,拿到 cookie 和 url,然后作为代码的参数
我把它写成了死循环…然后,运行这个代码。每次访问http://zzh.freeshell.ustc.edu.cn/game/newroom.php这个url,它就会返回一个新房间的地址,相当于完成了开房的动作。
以上这小段代码也是我经过一会时间修改才搞定的…
确认可行之后,我开了10个终端…一起刷,结果一个多小时后,网站就挂了。。。整个 freeshell 登不上去了。经神秘管理员重启之后,网页上已经无法新建房间,随机输入一个房间号都有一个叫做 root 的玩家在里面。
-78cbf7ee7ebe5a37
后续情节…
肇事者表示刚刚学会写爬虫所以拿了个好朋友的网站来玩…神秘的 freeshell 管理员其实是帮凶,他不仅指导我写脚本,而且在我开始攻击之后,突发奇想想要测试该 freeshell 的抗压能力…搞得该站长一直以为是有人通过 2081号 freeshell 攻击。我做了坏事,看对方没有反应,先是主动上去报了 bug(“哎呀,你的五子棋没法玩了耶”→好贱),最后被查出来以后主动承认并道歉…我表示一开始还挺兴奋,后面觉得这么做还是挺不好的…要是对方生气了呢,是吧?
“入门”是良好的动机,但是可能作用缓慢。如果你手里或者脑子里有一个项目,那么实践起来你会被目标驱动,而不会像学习模块一样慢慢学习。
另外如果说知识体系里的每一个知识点是图里的点,依赖关系是边的话,那么这个图一定不是一个有向无环图。因为学习A的经验可以帮助你学习B。因此,你不需要学习怎么样“入门”,因为这样的“入门”点根本不存在!你需要学习的是怎么样做一个比较大的东西,在这个过程中,你会很快地学会需要学会的东西的。当然,你可以争论说需要先懂python,不然怎么学会python做爬虫呢?但是事实上,你完全可以在做这个爬虫的过程中学习python
看到前面很多答案都讲的“术”——用什么软件怎么爬,那我就讲讲“道”和“术”吧——爬虫怎么工作以及怎么在python实现。
先长话短说summarize一下:
你需要学习
基本的爬虫工作原理
基本的http抓取工具,scrapy
Bloom Filter: Bloom Filters by Example
如果需要大规模网页抓取,你需要学习分布式爬虫的概念。其实没那么玄乎,你只要学会怎样维护一个所有集群机器能够有效分享的分布式队列就好。最简单的实现是python-rq: https://github.com/nvie/rq
rq和Scrapy的结合:darkrho/scrapy-redis · GitHub
后续处理,网页析取(grangier/python-goose · GitHub),存储(Mongodb)
以下是短话长说:
说说当初写的一个集群爬下整个豆瓣的经验吧。
1)首先你要明白爬虫怎样工作。
想象你是一只蜘蛛,现在你被放到了互联“网”上。那么,你需要把所有的网页都看一遍。怎么办呢?没问题呀,你就随便从某个地方开始,比如说人民日报的首页,这个叫initial pages,用$表示吧。
在人民日报的首页,你看到那个页面引向的各种链接。于是你很开心地从爬到了“国内新闻”那个页面。太好了,这样你就已经爬完了俩页面(首页和国内新闻)!暂且不用管爬下来的页面怎么处理的,你就想象你把这个页面完完整整抄成了个html放到了你身上。
突然你发现, 在国内新闻这个页面上,有一个链接链回“首页”。作为一只聪明的蜘蛛,你肯定知道你不用爬回去的吧,因为你已经看过了啊。所以,你需要用你的脑子,存下你已经看过的页面地址。这样,每次看到一个可能需要爬的新链接,你就先查查你脑子里是不是已经去过这个页面地址。如果去过,那就别去了。
好的,理论上如果所有的页面可以从initial page达到的话,那么可以证明你一定可以爬完所有的网页。
那么在python里怎么实现呢?
很简单
import Queue
initial_page = "http://www.renminribao.com"
url_queue = Queue.Queue()
seen = set()
seen.insert(initial_page)
url_queue.put(initial_page)
while(True): #一直进行直到海枯石烂
if url_queue.size()>0:
current_url = url_queue.get() #拿出队例中第一个的url
store(current_url) #把这个url代表的网页存储好
for next_url in extract_urls(current_url): #提取把这个url里链向的url
if next_url not in seen:
seen.put(next_url)
url_queue.put(next_url)
else:
break
写得已经很伪代码了。
所有的爬虫的backbone都在这里,下面分析一下为什么爬虫事实上是个非常复杂的东西——搜索引擎公司通常有一整个团队来维护和开发。
2)效率
如果你直接加工一下上面的代码直接运行的话,你需要一整年才能爬下整个豆瓣的内容。更别说Google这样的搜索引擎需要爬下全网的内容了。
问题出在哪呢?需要爬的网页实在太多太多了,而上面的代码太慢太慢了。设想全网有N个网站,那么分析一下判重的复杂度就是N*log(N),因为所有网页要遍历一次,而每次判重用set的话需要log(N)的复杂度。OK,OK,我知道python的set实现是hash——不过这样还是太慢了,至少内存使用效率不高。
通常的判重做法是怎样呢?Bloom Filter. 简单讲它仍然是一种hash的方法,但是它的特点是,它可以使用固定的内存(不随url的数量而增长)以O(1)的效率判定url是否已经在set中。可惜天下没有白吃的午餐,它的唯一问题在于,如果这个url不在set中,BF可以100%确定这个url没有看过。但是如果这个url在set中,它会告诉你:这个url应该已经出现过,不过我有2%的不确定性。注意这里的不确定性在你分配的内存足够大的时候,可以变得很小很少。一个简单的教程:Bloom Filters by Example
注意到这个特点,url如果被看过,那么可能以小概率重复看一看(没关系,多看看不会累死)。但是如果没被看过,一定会被看一下(这个很重要,不然我们就要漏掉一些网页了!)。 [IMPORTANT: 此段有问题,请暂时略过]
好,现在已经接近处理判重最快的方法了。另外一个瓶颈——你只有一台机器。不管你的带宽有多大,只要你的机器下载网页的速度是瓶颈的话,那么你只有加快这个速度。用一台机子不够的话——用很多台吧!当然,我们假设每台机子都已经进了最大的效率——使用多线程(python的话,多进程吧)。
3)集群化抓取
爬取豆瓣的时候,我总共用了100多台机器昼夜不停地运行了一个月。想象如果只用一台机子你就得运行100个月了…
那么,假设你现在有100台机器可以用,怎么用python实现一个分布式的爬取算法呢?
我们把这100台中的99台运算能力较小的机器叫作slave,另外一台较大的机器叫作master,那么回顾上面代码中的url_queue,如果我们能把这个queue放到这台master机器上,所有的slave都可以通过网络跟master联通,每当一个slave完成下载一个网页,就向master请求一个新的网页来抓取。而每次slave新抓到一个网页,就把这个网页上所有的链接送到master的queue里去。同样,bloom filter也放到master上,但是现在master只发送确定没有被访问过的url给slave。Bloom Filter放到master的内存里,而被访问过的url放到运行在master上的Redis里,这样保证所有操作都是O(1)。(至少平摊是O(1),Redis的访问效率见:LINSERT – Redis)
考虑如何用python实现:
在各台slave上装好scrapy,那么各台机子就变成了一台有抓取能力的slave,在master上装好Redis和rq用作分布式队列。
代码于是写成
#slave.py
current_url = request_from_master()
to_send = []
for next_url in extract_urls(current_url):
to_send.append(next_url)
store(current_url);
send_to_master(to_send)
#master.py
distributed_queue = DistributedQueue()
bf = BloomFilter()
initial_pages = "www.renmingribao.com"
while(True):
if request == ‘GET':
if distributed_queue.size()>0:
send(distributed_queue.get())
else:
break
elif request == ‘POST':
bf.put(request.url)
好的,其实你能想到,有人已经给你写好了你需要的:darkrho/scrapy-redis · GitHub
4)展望及后处理
虽然上面用很多“简单”,但是真正要实现一个商业规模可用的爬虫并不是一件容易的事。上面的代码用来爬一个整体的网站几乎没有太大的问题。
但是如果附加上你需要这些后续处理,比如
有效地存储(数据库应该怎样安排)
有效地判重(这里指网页判重,咱可不想把人民日报和抄袭它的大民日报都爬一遍)
有效地信息抽取(比如怎么样抽取出网页上所有的地址抽取出来,“朝阳区奋进路中华道”),搜索引擎通常不需要存储所有的信息,比如图片我存来干嘛…
及时更新(预测这个网页多久会更新一次)
如你所想,这里每一个点都可以供很多研究者十数年的研究。虽然如此,
“路漫漫其修远兮,吾将上下而求索”。
0、新手/喜欢练习/欢迎交流/邀请/我是看着这个问题下面的答案学习的
1、带着一个目的来学爬虫。#我的目的实现了…所以我来写这个回答了。
2、不要怂就是干!系统学习固然好,直接写一个项目出来效果更加简单粗暴!(不过自己现在的水平写出来都是流水一般的面向过程的代码,代码的重复部分太多,正在回过头去学习面向对象编程,学习类和方法的使用。不过我还是坚定地认为入门的时候应该直接简单粗暴地实践一个项目)
3、哪里不会搜哪里!哪里报错改哪里!相信我你遇到的99%的问题都能从网上找到相似的问题,你需要做的就是写代码!搜问题!调BUG!你搜不到解决办法的情况下,80%的情况是你搜索的姿势不对,另外20%可能需要你自己动动脑子,换个思路去做。
获取数据
假如我们获取到了单页数据,那么使用正则可以很简单地获取到想要的数据,具体参见代码。
我们需要做的,其实是获取那些需要爬取的URL。
通过上面对于网址的分析我们可以发现,网址的组成为domain/answer/ans_id/voters_profile?total=xxx&offset=xx&…
后面那堆乱码不重要,重要的是total和offset,每次会展示出10个用户的数据,所以我们只需要获取到点赞的总数total,就可以知道需要循环多少步(total/10),注意从是0开始,不然会漏掉前十个数据。
而这也就是我在zhihu-vote.py中的做法。其余的部分就没有什么难度了,入门的同学应该都可以看懂。
3、改进
我们在zhihu-vote.py中通过构造地址的方法来,通过循环实现对所有voters_profile的遍历。但是如果我们了解json的知识的话,我们可以发现其实每个页面都是json格式的。
其中最关键的地方在于next。我们会发现其实每个页面中都包含了下一页的地址!这样我们是不是可以让爬虫每爬一页自己找到一个地址,然后自己去爬下一页呢?
可是这样做有一个问题,如何控制循环呢?
假如我们去看最后一个页面的话,会发现是这样的。
注意这里的next值为空。
而我们知道(不知道的你现在知道了),空字符串在作为条件判断时相当于False
所以我写了zhihu-voteV2.py
其中核心的改动是
Vote_url = Zhihu + ‘answer/’ + ans_id +’/voters_profile’
h = s.get(Vote_url)
html = h.content.encode(‘utf-8′)
target = json.loads(html)
while target[‘paging’][‘next’]:
Vote_url = ‘http://www.zhihu.com’+target[‘paging’][‘next’]
这样就实现了程序每次爬取页面时从页面中获取地址,而不是人为构造地址循环。下面是原来的做法。
for num in range (0,page_num):
Vote_url = Zhihu + ‘answer/’ + ans_id +’/voters_profile?total=’+str(total)+’&offset=’+str(num)+’0′
讲实话我不知道这两种写法哪种好,但我还是蛮高兴自己发现了第二种做法。
于是我做了一个运行时间的测试…提车了,下一步需要做什么? – 车海沉浮高永强的回答 16K的赞
运行结果如下:
构造地址的办法用时451秒,第二种办法用时251秒。
……我不知道为什么方法二会比方法一快,可能是网速吧……QAQ。有了解的前辈还望告知原因…
到这里也就结束了。最后的结果是写入excel的,有知友说让我去学习csv,已经在看了,不过这次还是用的让人又爱又恨的excel。
按照惯例写To-dos:
完善Github的文档说明
想办法看能不能自动获取那个蛋疼的ans_id就不用每次都手动抓包了,selenium?我不会用啊TAT
在点赞的页面我们只能得到用户的4个数据,也就是赞同、感谢、提问、回答,有些时候我们或许想知道他的关注人数和被关注人数…然而那个得到用户的页面中去爬取了。不过想通过用户URL得到用户的具体数据是有现成的轮子的……egrcc/zhihu-python · GitHub (PY2) @egrcc 和7sDream/zhihu-py3 · GitHub(PY3) @7sDream 我想办法看怎么把我现在获取答案点赞用户信息的方法pull给他们…直接调用他们的User类就ok了~
抓特别多的数据时考虑多线程和gzip…?接下来就要学这个了…会的话我就用了…还记得我去爬知乎赞数最高的答案…一个答案爬了30分钟…
——更新完毕,大家学习愉快,共同进步——
——Windows 平台Py2编码问题畸形但有效解法——
在..Python27Libsite-packages下新建sitecustomize.py
添加代码
import sys
sys.setdefaultencoding("utf-8")
新手学习python爬取网页先用下面4个库就够了:(第4个是实在搞不定用的,当然某些特殊情况它也可能搞不定)
1. 打开网页,下载文件:urllib
2. 解析网页:BeautifulSoup,熟悉JQuery的可以用Pyquery (感谢 @李林蔚 的建议)
3. 使用Requests来提交各种类型的请求,支持重定向,cookies等。
4. 使用Selenium,模拟浏览器提交类似用户的操作,处理js动态产生的网页
这几个库有它们各自的功能。配合起来就可以完成爬取各种网页并分析的功能。具体的用法可以查他们的官网手册(上面有链接)。
做事情是要有驱动的,如果你没什么特别想抓取的,新手学习可以从这个爬虫闯关网站开始
,目前更新到第五关,闯过前四关,你应该就掌握了这些库的基本操作。
实在闯不过去,再到这里看题解吧,第四关会用到并行编程。(串行编程完成第四关会很费时间哦),第四,五关只出了题,还没发布题解。。。
学完这些基础,再去学习scrapy这个强大的爬虫框架会更顺些。
1.抓取
py的urllib不一定去用,但是要学,如果你还没用过的话。
比较好的替代品有requests等第三方更人性化、成熟的库,如果pyer不了解各种库,那就白学了。
抓取最基本就是拉网页回来。
如果深入做下去,你会发现要面对不同的网页要求,比如有认证的,不同文件格式、编码处理,各种奇怪的url合规化处理、重复抓取问题、cookies跟随问题、多线程多进程抓取、多节点抓取、抓取调度、资源压缩等一系列问题。
所以第一步就是拉网页回来,慢慢你会发现各种问题待你优化。
2.存储
抓回来一般会用一定策略存下来,而不是直接分析,个人觉得更好的架构应该是把分析和抓取分离,更加松散,每个环节出了问题能够隔离另外一个环节可能出现的问题,好排查也好更新发布。
那么存文件系统、SQLorNOSQL数据库、内存数据库,如何去存就是这个环节的重点。
你可以选择存文件系统开始,然后以一定规则命名。
3.分析
对网页进行文本分析,提取链接也好,提取正文也好,总之看你的需求,但是一定要做的就是分析链接了。
可以用你认为最快最优的办法,比如正则表达式。
然后将分析后的结果应用与其他环节:)
4.展示
要是你做了一堆事情,一点展示输出都没有,如何展现价值?
所以找到好的展示组件,去show出肌肉也是关键。
如果你为了做个站去写爬虫,抑或你要分析某个东西的数据,都不要忘了这个环节,更好地把结果展示出来给别人感受。
网页的源码经常会改变的!网页的源码经常会改变的!网页的源码经常会改变的!重要的事情说三遍。所以你看到的那些14年甚至15年的东西很多都是不能用的。这个不知道误导了多少的新手,我在某些14年的文章下面的评论还看到16年有人在评论说怎么怎么用不了,原因就是网站的源码改了呗。
入门爬虫其实很快,可能一个下午就够了,但是要精通非常困难。如果你有兴趣的话看完我这一篇文章,便能自己写一个爬虫了。
接下来是教程。很蛋疼的是知乎不支持markdown所以很多的东西都不能用程序源码的形式只能用普通文本的形式,大家将就一下。
先给大家介绍一下爬虫,爬虫呢很形象,就是说你写的程序就是一个小虫子,在网页上爬啊爬,然后把自己需要的东西保存下来了。
需要工具:python2,因为是入门,所以这次用到的两个包都是python自带的
import urllib2
import re
简单介绍一下两个包,urllib2是用来解析网站的url的,re是一个正则表达式模块,在这个里面我用它来当比较。
首先我们要定义一个类,插一下话,大家最好都用你需要这个类或者变量做什么的英语来命名,这样维护这个代码,或者让别人来读你的代码的时候都方便很多。定义一个类你可以随便叫他什么,在这里我给他起个名字叫spiderMode,蜘蛛模块。他有四个属性,页,页面,能否访问,以及一个处理类,等会儿会讲到的。因为这次我们只要爬一个网页,所以页和页面其实也都可以不要,而且保证url是有效的,enable也可有可无。
class spiderMode:
def __init__(self):
self.page = 1
self.pages = []
self.enable = False
self.sub = Sub()
接下来就是来定义我们最重要的函数啦,用函数来抓取页面。我们用最最最最简单的糗事百科来作为例子。那么url就定义如下
sUrl = "http://www.qiushibaike.com/"
接下来是一个我们可以说是伪装,为什么呢因为你用python去访问人家的网站人家会不开心不让你访问,所以你就要披上一层皮,这层皮我们叫headers,定义如下
userAgent = ‘Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)’
headers = { ‘User-Agent': userAgent}
好啦,这时候我们就可以去请求访问啦,首先要request一下,这个函数的作用就是request这个url,也就是我们定义的sUrl,headers是我们的伪装。
request = urllib2.Request(sUrl, headers=headers)
之后我们就可以打开这个链接
response = urllib2.urlopen(request)
用一个变量来读这个网页这时候你可以用print来读出一下,这时候出来的就是网页的源码。
sPage = response.read()
光有源码没有用,我们需要将它转变为能够处理的格式,因为本来是utf-8的编码,因此要经过一步转码,现在unicodePage里的东西就是我们需要的网页的所有源码啦
unicodePage = sPage.decode("utf-8")
这时候我们就需要找到所有的我们需要的信息,我们要来分析一下网页的源码,发现所有的笑话都是这么样的格式的
<div class="content">
女领导把我叫到办公室,目光在我身上扫射了一遍道:“听说你还没女朋友啊,要不要给你介绍个?”<br/>听得我有点莫名其妙,一向身为工作狂她,啥时候开始八卦起来了?但内心略有一丝欢喜,慌忙的点了点头,只见其接着道:“我刚离婚,考虑下不?”……
</div>
好了我们就能写出我们的re表达式,这一句的意思是在unicodePage中找到所有的夹在那两个东西之间的信息。(.*?)表示的是任意循环一次的信息。?是表示循环一次,如果你不加?的话就会直接定位第一个<div>和最后一个</div>了,这不是我们想看到的。
myItems = re.findall(‘<div class="content">(.*?)</div>’,unicodePage,re.S)
其实这时候就做的差不多的,这时候我们就可以直接输出了。但是看一下刚才的源码中有几个</br>,这是什么呢?其实是换行符,所以我们要定义一个类来去掉换行符,并且加入我们输出的时候用刀的换行符"n"。用以下代码实现。
class Sub:
replaceBr = re.compile(‘<br/>’)
def replace(self,x):
x = re.sub(self.replaceBr,"n",x)
return x.strip()
这也是我们刚才提到的sub类,可以用来整理格式。最后就是输出啦,顺便再编一个号。
num=1
for item in myItems:
print str(num) +’. ‘ + self.sub.replace(item) +’n’
num += 1
整个程序就是这么的简单,下面我贴一下完整的源码方便大家看一下。
一个爬虫就这么做好了。当然这个是最最简单的,之后怎么进阶就需要大家去思考啦。
Requests基础发送请求
所有requests请求均返回response对象
requests.get(url) 通过get方式发送请求
参数可以通过url+?+key=value&…方式添加
也可以dict={key:value},requests.get(url,params=dict)方式添加
requests.post(url) 通过post方式发送请求
参数可以通过dict={key:value}, requests.post(url,data=dict)方式添加
如果不以表单形式提交,request.post(url,data=json.dumps(dict))
requests.get(url, headers = headers) 定制请求头
headers的内容可以审查里查看request header里内容
requests.get(url,cookies = cookies) 发送cookies到服务器
cookies = dict(Cookies=’working’) key和value都从request hearder里查看(用于免登陆)
response.cookies可以获得可用于登陆的cookie,但是只能从post方法的requests得到cookies
requests.utils.dict_from_cookiejar(cookies) 将cookie转换为dict
requests.get(url, proxies=proxies) 使用代理
requests.get(url, timeout=n) n秒后停止等待响应
响应内容
r= requests.get(url)
r.url 传递的地址
r.text 文本格式内容
r.json json格式内容
r.content 字节格式内容(常用)
r.status_code 响应状态码
正常 200 requests.code.ok
r.headers 服务器响应头
Session
Session保持用户的登录信息
s=requests.Session() 新建Session
s.headers.update({‘x-test’: ‘true’}) 之后的不用在参数里加上headers
s.post(url,data)得到cookie后,再s.get(url)可以不通过cookie就可以访问页面
如何反爬虫
cookies池,更换cookie意味着更换用户
proxies池,更换proxy意味着更换IP
header中伪装浏览器,加入User-Agent及Referer
设置延迟,time.sleep(1)
正则表达式常用符号
.:匹配任意字符,换行符n除外, 类似一个占位符
*:匹配前一个字符0次或无限次
?:匹配前一个字符0次或1次
+:匹配前一个字符1词或无限次
.*:贪心算法,能找到多少是多少
.*?:非贪心算法
d:匹配数字
[abc]:abc任意一个
{m,n}:匹配前一个字符m到n次
| :匹配|左右任意一个
(.*?) :输出符合条件的,将()里结果返回
常用方法
findall:匹配所有符合规律的内容,返回包含结果的列表;
第一个参数是pattern,第二个是查找范围
re.S作为findall的第三个参数,让 . 匹配n
以列表返回将所有的结果
content = re.findall(r'<td valign="top">(.*?)</a>’, html, re.S)
search:匹配并提取第一个符合规律的内容,返回一个正表达式对象
group(1)表示取出()里面的
url = re.search(r'<a href="(.*?)">’, each, re.S).group(1)
sub:替换符合规律的内容,返回替换后的值
第一个参数是pattern,第二个参数替换的值,第三个是替换变量
test_str = re.sub(u’美元|人民币|元|本金|代理|的’, ”, test_str)
XPATH
不同于正则表达式基于内容,XPATH基于结构
使用lxml包
from lxml import etree selector = etree.HTML(html) selector.xpath(‘//*[@id="pagelist"]/form/div/text()’)
BeautifulSoup创建对象
soup = BeautifulSoup(html) 创建对象
soup = BeautifulSoup(open(‘index.html’)) 从文件中创建对象
print soup.prettify() 打印soup对象 格式化输出
Tag
Tag就是HTML里面的标签
print soup.[Tag] 查找第一个符合要求的标签
每个Tag对象都有name和attrs两个属性,attrs可以以dict形式将所有属性打印出来
print soup.[Tag][key] 得到单独的某个属性值 等价于 soup.[Tag].get(‘key’)
可以通过soup.[Tag][key]=value 修改值
可以通过del soup.p[key]删除值
其他对象
NavigableString对象 soup.p.string 得到标签里面的内容
BeautifulSoup对象 表示整个文档的内容,也有name和attrs两个属性即 soup.attrs
Comment 对象 即注释内容,通过type(soup.a.string)==bs.element.Comment判断
遍历文件树
.content 属性可以将tag的子节点以列表的方式输出,通过列表索引获得某个元素
.children 返回的不是一个list, 通过遍历查看内容
.descendants 返回tag的所有子孙节点,需要遍历获得内容
.strints 获得多个内容,需要便利查看
.sripped_strings 可以去除多余的空格
.parent 父节点
.parents 全部父节点,需要遍历
.next_sibling 属性获取了该节点的下一个兄弟节点,.previous_sibling 则与之相反
本节点处在统一级的节点
际文档中的tag的 .next_sibling 和 .previous_sibling 属性通常是字符串或空白,因为空白或者换行也可以被视作一个节点
.next_siblings 和 .previous_siblings 属性可以对当前节点的兄弟节点迭代输出
.next_element .previous_element 与 .next_sibling .previous_sibling 不同,它并不是针对于兄弟节点,而是在所有节点,不分层次
.next_elements 和 .previous_elements 的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样
搜索文件树find_all( name , attrs , recursive , text , **kwargs )
name 参数 查找名为name的tag, 字符串对象会过滤掉
传字符串 print soup.find_all(‘a’) 会查找与字符串完整匹配的内容
传正则表达式 soup.find_all(re.compile(“^b”)) 查找b开头的标签
传列表 soup.find_all([“a”, “b”]) 会将与列表中任一元素匹配的内容返回
传True 可以匹配任何值 查找所有的tag
传方法 如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False
def has_class_but_no_id(tag): return tag.has_attr(‘class’) and not tag.has_attr(‘id’)
keyword参数 如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup会搜索每个tag的”id”属性
可以使用多个制定名字的参数同时过滤
soup.find_all(href=re.compile("elsie"), id=’link1′)
想用 class 过滤,不过 class 是 python 的关键词,这怎么办?加个下划线就可以
soup.find_all("a", class_="sister")
可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag
data_soup.find_all(attrs={"data-foo": "value"})
text参数 可以搜搜文档中的字符串内容.与 name 参数的可选值一样, text 参数接受 字符串 , 正则表达式 , 列表, True
limit参数 limit=n 只返回前n个结果
recursive 设置recursive=False 则只检查子节点
其他方法
find( name , attrs , recursive , text , **kwargs ) 直接返回结果而不是列表
类似的还有搜索父节点、兄弟节点、旁边节点等等
一、网络爬虫的定义
网络爬虫,即Web Spider,是一个很形象的名字。
把互联网比喻成一个蜘蛛网,那么Spider就是在网上爬来爬去的蜘蛛。
网络蜘蛛是通过网页的链接地址来寻找网页的。
从网站某一个页面(通常是首页)开始,读取网页的内容,找到在网页中的其它链接地址,
然后通过这些链接地址寻找下一个网页,这样一直循环下去,直到把这个网站所有的网页都抓取完为止。
如果把整个互联网当成一个网站,那么网络蜘蛛就可以用这个原理把互联网上所有的网页都抓取下来。
这样看来,网络爬虫就是一个爬行程序,一个抓取网页的程序。
网络爬虫的基本操作是抓取网页。
那么如何才能随心所欲地获得自己想要的页面?
我们先从URL开始。
二、浏览网页的过程
抓取网页的过程其实和读者平时使用IE浏览器浏览网页的道理是一样的。
比如说你在浏览器的地址栏中输入 http://www.baidu.com 这个地址。
打开网页的过程其实就是浏览器作为一个浏览的“客户端”,向服务器端发送了 一次请求,把服务器端的文件“抓”到本地,再进行解释、展现。
HTML是一种标记语言,用标签标记内容并加以解析和区分。
浏览器的功能是将获取到的HTML代码进行解析,然后将原始的代码转变成我们直接看到的网站页面。
三、URI和URL的概念和举例
简单的来讲,URL就是在浏览器端输入的 http://www.baidu.com 这个字符串。
在理解URL之前,首先要理解URI的概念。
什么是URI?
Web上每种可用的资源,如 HTML文档、图像、视频片段、程序等都由一个通用资源标志符(Universal Resource Identifier, URI)进行定位。
URI通常由三部分组成:
①访问资源的命名机制;
②存放资源的主机名;
③资源自身 的名称,由路径表示。
如下面的URI:
http://www.why.com.cn/myhtml/html1223/
我们可以这样解释它:
①这是一个可以通过HTTP协议访问的资源,
②位于主机 http://www.webmonkey.com.cn上,
③通过路径“/html/html40”访问。
四、URL的理解和举例
URL是URI的一个子集。它是Uniform Resource Locator的缩写,译为“统一资源定位 符”。
通俗地说,URL是Internet上描述信息资源的字符串,主要用在各种WWW客户程序和服务器程序上。
采用URL可以用一种统一的格式来描述各种信息资源,包括文件、服务器的地址和目录等。
URL的一般格式为(带方括号[]的为可选项):
protocol :// hostname[:port] / path / [;parameters][?query]#fragment
URL的格式由三部分组成:
①第一部分是协议(或称为服务方式)。
②第二部分是存有该资源的主机IP地址(有时也包括端口号)。
③第三部分是主机资源的具体地址,如目录和文件名等。
第一部分和第二部分用“://”符号隔开,
第二部分和第三部分用“/”符号隔开。
第一部分和第二部分是不可缺少的,第三部分有时可以省略。
五、URL和URI简单比较
URI属于URL更低层次的抽象,一种字符串文本标准。
换句话说,URI属于父类,而URL属于URI的子类。URL是URI的一个子集。
URI的定义是:统一资源标识符;
URL的定义是:统一资源定位符。
二者的区别在于,URI表示请求服务器的路径,定义这么一个资源。
而URL同时说明要如何访问这个资源(http://)。
下面来看看两个URL的小例子。
1.HTTP协议的URL示例:
使用超级文本传输协议HTTP,提供超级文本信息服务的资源。
例:http://www.peopledaily.com.cn/channel/welcome.htm
其计算机域名为www.peopledaily.com.cn。
超级文本文件(文件类型为.html)是在目录 /channel下的welcome.htm。
这是中国人民日报的一台计算机。
例:http://www.rol.cn.net/talk/talk1.htm
其计算机域名为www.rol.cn.net。
超级文本文件(文件类型为.html)是在目录/talk下的talk1.htm。
这是瑞得聊天室的地址,可由此进入瑞得聊天室的第1室。
2.文件的URL
用URL表示文件时,服务器方式用file表示,后面要有主机IP地址、文件的存取路 径(即目录)和文件名等信息。
有时可以省略目录和文件名,但“/”符号不能省略。
例:file://ftp.yoyodyne.com/pub/files/foobar.txt
上面这个URL代表存放在主机ftp.yoyodyne.com上的pub/files/目录下的一个文件,文件名是foobar.txt。
例:file://ftp.yoyodyne.com/pub
代表主机ftp.yoyodyne.com上的目录/pub。
例:file://ftp.yoyodyne.com/
代表主机ftp.yoyodyne.com的根目录。
爬虫最主要的处理对象就是URL,它根据URL地址取得所需要的文件内容,然后对它 进行进一步的处理。
因此,准确地理解URL对理解网络爬虫至关重要。
学习爬虫可以从下面一些知识点入手学习。
1、http相关知识。
2、浏览器拦截、抓包。
3、python2 中编码知识,python3 中bytes 和str类型转换。
4、抓取javascript 动态生成的内容。
4、模拟post、get,header等
5、cookie处理,登录。
6、代理访问。
7、多线程访问、python 3 asyncio 异步。
8、正则表达式、xpath等
等等。。。。
还有scrapy requests等第三方库的使用。
推荐看看黄哥讲的“python爬虫联想词视频”,播放地址,请自己搜索。
Python 爬虫入门直接实战就好
当你通过网上的各种教程学会了,爬个妹子图,糗事百科之类的简单的网站的时候,那么你对于爬虫的基本逻辑结构就有了一个大概的了解,学会了 urllib/urllib2 (Py2 和 Py3 urllib有区别),但是很快,你在某个阳光明媚的下午邂逅了优雅的一塌糊涂 requests ,你突然发现,ci‘ao!这才是给Pythoner 用的啊,没错 requests Requests: HTTP for Humans 就是为人类写的,于是你在心中默默的送给 urllib 一个万个“你大爷的”,然后和挥手惜别。
当你完成一个网站的模拟登录,基本就可以算是是入门了,因为你对于网站的各种 http/https 请求已经非常熟悉了,懂得了 get post cookies session 这些都是什么鬼,顺便学会了 Python 的异常处理,如果对 Python 处理还不了解,慕课网-国内最大的IT技能学习平台 有个 Python 异常处理的视频
当然在模拟登录网站的过程中,你开始接触到正则表达式,知道 .* 和 .*? 了的区别和 () 的作用,慢慢地,你已经不能忍受你写的复杂的正则表达式了,这时候 beautifulsoup 和 lxml 来到了你的身边,就是你的情人一样,带给你不一样的美好体验,可是永远不要忘记 re 才是你的妻子。
当然模拟登录的过程中,你还会掌握一门挖掘机技术,不是是抓包技术,额,等等,抓包是什么鬼。你在论坛和QQ群,求助怎么模拟登录和抓取异步加载的网页的时候,大神们都会用 “抓包分析下” 甩你一脸。经常使用的抓包工具有 浏览器(推荐 Chrome ), fidder, wireshark 等抓包工具。对于爬虫遇到的网络抓包分析, chrome 浏览器功能足够了,(在浏览器的隐身模式下,抓包分析更佳哦,ctrl+shift+N 快捷键)。
当你遇到抓包分析,解决不了的问题的时候,你将会修炼一门在百毒的帮助下读懂js源码的能力,至于html css 这些,你早就和他们有了无数次的亲密接触了,虽然写不出来,但是理解他们应该没有问题。
一二三四五,你可以上山打老虎了。等等,少年,送你一本葵花宝典 Web Scraping with Python (豆瓣)
首先讲一下最基础的开发环境,你得电脑里都有python3.X(下文都是基于3.X写的)并且你可以熟练使用基础的python,然后你得有 Beautiful Soup (Beautiful Soup Documentation) 美丽的汤!! 具体怎么安装自行搜索。Documentation里也有。
我们来试试爬一下新华网_让新闻离你更近 (随机选的栗子!)
from urllib.request import urlopen
from bs4 import BeautifulSoup
在爬之前我们得先把必备的modules 导进来!
urlopen 的功用就是打开一个网址的意思!
beautiful soup就是一碗好喝的烫!
不,beautiful soup 是解析html用的。
如果你问我html是什么,先出门左拐自学一下http://www.w3schools.com/html/
url = "http://www.xinhuanet.com/" #define your url
html = urlopen(url) #get the url
简单易懂,这两行就是提取了新华网首页的html
最好单独放置你的url,让urlopen里的值是个variable,这样将来东西多了也方便。
比如你有一个list的url,那么将来你就可以
url = [‘http://www.google.com’,
‘http://www.baidu.com’,’http://www.xinhuanet.com/’]
def getURL():
return url.pop()
html = urlopen(getURL())
这就是个例子,具体怎么处理还是要视具体情况而定
好,我们现在手里有html了。但是一堆tag电脑也是不懂得,这时就是美丽的烫大显身手的时候了!
bsObj = BeautifulSoup(html, "html.parser") #get a bs object
这一行很好理解,bsObj是一个美丽的烫的object。你问我object什么意思? 先出门右拐学习一下9. Classes
object里的两个parameters,第一个html就是把之前我们到手的html放进美丽的烫里,让他帮咱们分析。后边的可以忽略,就是个美丽的烫的一个param。
至此为止我们已经预备好了提取这个页面里的任何一个东西了。
print(bsObj.title)
这行就会打印出这个页面的标题:新华网_让新闻离你更近
这个很简单,那么具体的东西怎么找呢?
这里就要借助我们Chrome的力量了
右键点Inspect
比如我们想锁定全部“第三届世界媒体峰会多哈开幕”这个link
那么我们就可以
content = bsObj.find("h3", class_="focusWordBlue unclick")
link = content.find("a")
href = link.get("href")
(这里假设本网页只有一个<h3> 的tag的class是"focusWordBlue unclick")
第一句,在bs这个object里找到符合如下条件的tag:
名称是h3, class是focusWordBlue unclick
这里的关键部分就是find这个function。美丽的烫提供的,自动帮助你寻找符合你给出的条件的tag
之后第二次使用find这个function 找到第一个hyperlink 也就是<a>这个link
bs找到每一个tag都会返回一个类似于dictionary type 的variable,
所以这时我们可以直接用get这个function。
get"href"就相当于get dictionary 里的get(key)
<a href = http://www.xinhuanet.com/world/wms2016/index.htm>
所以这里返回的结果就是 http://xinhuanet.com/world/wms2016/index.htm这个link
print(href)
就可以看到了。
如果想继续往下一层爬可以
html = urlopen(href)
这里就涉及了一个重要的概念 Recursion
Recursion (computer science)
要弄好爬虫要先学彻底搞清楚这个概念。
入门其实到这里就完结了,你现在已经可以自由的爬取任意的单一页面的html了!
当然了 美丽的烫还有许许多多的非常有用的function,建议多多阅读documentation!!里边有详细的讲解!
从基本的单页面爬虫,依次涉及了BeautifulSoup,Html Parsing,Regex,到使用框架Scrapy进行爬虫;
还介绍了各种APIs,Json,以及数据的存储。
这还仅仅是第一部分,后续又用了很大篇幅介绍了Advanced Scraping。
相信一本知识连续的书一定比看各种博客效果好的多!
很早之前为统计电商网站价格,用过urllib+BeautifulSoup, 可以很顺利完成任务。但对其繁琐的逻辑步骤很恼火。由于urllib过于复杂,各种opener、cookie handler让你不得不重复很多行代码,这增加了开发时间。你可以换成requests,更加human readable,不过还是需要大量的coding。BeatifulSoup很强大,你可以抓取任何html格式的text或attribute。不过如果你对xpath不熟悉,写几十行仅仅作为抓取的代码是很正常的。另外储存数据的代码也需要消耗不少时间。
最近我在用scrapy,它几乎克服了前者的所有缺点。简单到难以置信。框架是定义好的,不用你绞尽脑汁设计逻辑结构。你唯一需要做的是点开几个预设template的py文件,修改成你需要的即可。你只需告诉爬虫你需要的URL,spider会自动进行请求(另外它还支持用户密码登录模式)。对于抓取部分,可以用xpath和css,对于熟悉前段开发的来说简直是天堂。储存也是相当容易,它自带的export功能可让你储存XML json csv(喜欢mongodb的朋友请去安装scrapy-mongodb,不用写任何代码操控pymongo)。强烈推荐scrapy,使用过的最好的爬虫软件
1.学习这几个函数
urllib2.urlopen
re.compile
一个是从指定url读去内容
另一个是正则表达式获取特定内容
2.弄懂如下代码
#coding: utf8
import urllib2
import re
import StringIO
import gzip
ua = { #’User-Agent':’Mozilla/5.0 (compatible; Googlebot/2.1; +Googlebot – Webmaster Tools Help)’,
‘User-Agent':’Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11′,
‘Connection': ‘close’,
‘Accept-Language': ‘zh-CN,zh;q=0.8′,
‘Accept-Encoding': ‘gzip,deflate,sdch’,
‘Accept': ‘text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8′,
‘Accept-Charset': ‘GBK,utf-8;q=0.7,*;q=0.3′,
‘Cache-Control': ‘max-age=0′,
}
def get_html(url_address):
”’open url and read it”’
req_http = urllib2.Request(url_address,headers=ua)
#req_http = urllib2.Request(url_address)
html = urllib2.urlopen(req_http).read()
return html
def controller():
”’make url list and download page”’
urlt = ‘http://www.matrix67.com/blog/page/%d’
reget = re.compile(‘(<div class="post-wrapper.*?)<p class="pagination">’,re.DOTALL)
fp=open("org.txt","w+")
for i in range (1,131):
html_c = get_html(urlt% (i))
print urlt%(i)
html_c = gzip.GzipFile(fileobj=StringIO.StringIO(html_c)).read()
res = reget.findall(html_c)
for x in res :
fp.write(x)
fp.write("nnn")
fp.close();
return
controller()
好了,你可以去改变世界了
翻下 搜索引擎技术基础 (豆瓣) 中百度爬虫的一个基本架构,了解爬虫的构成
通过 Python 下的 Scrapy | An open source web scraping framework for Python 框架快速完成简单的爬虫 (可参考我去年对 scrapy 的一个简单封装 flyer103/autospider · GitHub)
之后有不同的方向:
研究 headless browser 技术,自动处理页面中的 js 请求等。可参考我之前的总结的两种实现 github.com 的页面 和效率比较高的 PhantomJS: Headless WebKit with JavaScript API (看文档就会了,一般还需要与 Squid 结合使用。若想与 Python 结合,可参考我在 stackoverflow 上的提问 Is there a way to use PhantomJS in Python?)
研究分布式爬虫的实现,主要还是根据 1) 中那本书中提到的架构思想 (个人打算明年一月份时实现一个类似的)
Python入门网络爬虫之精华版
Python学习网络爬虫主要分3个大的版块:抓取,分析,存储
另外,比较常用的爬虫框架Scrapy,这里最后也详细介绍一下。
首先列举一下本人总结的相关文章,这些覆盖了入门网络爬虫需要的基本概念和技巧:宁哥的小站-网络爬虫
当我们在浏览器中输入一个url后回车,后台会发生什么?比如说你输入宁哥的小站(fireling的数据天地)专注网络爬虫、数据挖掘、机器学习方向。,你就会看到宁哥的小站首页。
简单来说这段过程发生了以下四个步骤:
查找域名对应的IP地址。
向IP对应的服务器发送请求。
服务器响应请求,发回网页内容。
浏览器解析网页内容。
网络爬虫要做的,简单来说,就是实现浏览器的功能。通过指定url,直接返回给用户所需要的数据,而不需要一步步人工去操纵浏览器获取。
抓取
这一步,你要明确要得到的内容是什么?是HTML源码,还是Json格式的字符串等。
1. 最基本的抓取
抓取大多数情况属于get请求,即直接从对方服务器上获取数据。
首先,Python中自带urllib及urllib2这两个模块,基本上能满足一般的页面抓取。另外,requests也是非常有用的包,与此类似的,还有httplib2等等。
Requests:
import requests
response = requests.get(url)
content = requests.get(url).content
print "response headers:", response.headers
print "content:", content
Urllib2:
import urllib2
response = urllib2.urlopen(url)
content = urllib2.urlopen(url).read()
print "response headers:", response.headers
print "content:", content
Httplib2:
import httplib2
http = httplib2.Http()
response_headers, content = http.request(url, ‘GET’)
print "response headers:", response_headers
print "content:", content
此外,对于带有查询字段的url,get请求一般会将来请求的数据附在url之后,以?分割url和传输数据,多个参数用&连接。
data = {‘data1′:’XXXXX’, ‘data2′:’XXXXX’}
Requests:data为dict,json
import requests
response = requests.get(url=url, params=data)
Urllib2:data为string
import urllib, urllib2
data = urllib.urlencode(data)
full_url = url+’?’+data
response = urllib2.urlopen(full_url)
相关参考:网易新闻排行榜抓取回顾
参考项目:网络爬虫之最基本的爬虫:爬取网易新闻排行榜
2. 对于登陆情况的处理
2.1 使用表单登陆
这种情况属于post请求,即先向服务器发送表单数据,服务器再将返回的cookie存入本地。
data = {‘data1′:’XXXXX’, ‘data2′:’XXXXX’}
Requests:data为dict,json
import requests
response = requests.post(url=url, data=data)
Urllib2:data为string
import urllib, urllib2
data = urllib.urlencode(data)
req = urllib2.Request(url=url, data=data)
response = urllib2.urlopen(req)
2.2 使用cookie登陆
使用cookie登陆,服务器会认为你是一个已登陆的用户,所以就会返回给你一个已登陆的内容。因此,需要验证码的情况可以使用带验证码登陆的cookie解决。
import requests
requests_session = requests.session()
response = requests_session.post(url=url_login, data=data)
若存在验证码,此时采用response = requests_session.post(url=url_login, data=data)是不行的,做法应该如下:
response_captcha = requests_session.get(url=url_login, cookies=cookies)
response1 = requests.get(url_login) # 未登陆
response2 = requests_session.get(url_login) # 已登陆,因为之前拿到了Response Cookie!
response3 = requests_session.get(url_results) # 已登陆,因为之前拿到了Response Cookie!
相关参考:网络爬虫-验证码登陆
参考项目:网络爬虫之用户名密码及验证码登陆:爬取知乎网站
3. 对于反爬虫机制的处理
3.1 使用代理
适用情况:限制IP地址情况,也可解决由于“频繁点击”而需要输入验证码登陆的情况。
这种情况最好的办法就是维护一个代理IP池,网上有很多免费的代理IP,良莠不齐,可以通过筛选找到能用的。对于“频繁点击”的情况,我们还可以通过限制爬虫访问网站的频率来避免被网站禁掉。
proxies = {‘http':’http://XX.XX.XX.XX:XXXX’}
Requests:
import requests
response = requests.get(url=url, proxies=proxies)
Urllib2:
import urllib2
proxy_support = urllib2.ProxyHandler(proxies)
opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler)
urllib2.install_opener(opener) # 安装opener,此后调用urlopen()时都会使用安装过的opener对象
response = urllib2.urlopen(url)
3.2 时间设置
适用情况:限制频率情况。
Requests,Urllib2都可以使用time库的sleep()函数:
import time
time.sleep(1)
3.3 伪装成浏览器,或者反“反盗链”
有些网站会检查你是不是真的浏览器访问,还是机器自动访问的。这种情况,加上User-Agent,表明你是浏览器访问即可。有时还会检查是否带Referer信息还会检查你的Referer是否合法,一般再加上Referer。
headers = {‘User-Agent':’XXXXX’} # 伪装成浏览器访问,适用于拒绝爬虫的网站
headers = {‘Referer':’XXXXX’}
headers = {‘User-Agent':’XXXXX’, ‘Referer':’XXXXX’}
Requests:
response = requests.get(url=url, headers=headers)
Urllib2:
import urllib, urllib2
req = urllib2.Request(url=url, headers=headers)
response = urllib2.urlopen(req)
4. 对于断线重连
不多说。
def multi_session(session, *arg):
retryTimes = 20
while retryTimes>0:
try:
return session.post(*arg)
except:
print ‘.’,
retryTimes -= 1
或者
def multi_open(opener, *arg):
retryTimes = 20
while retryTimes>0:
try:
return opener.open(*arg)
except:
print ‘.’,
retryTimes -= 1
这样我们就可以使用multi_session或multi_open对爬虫抓取的session或opener进行保持。
5. 多进程抓取
这里针对华尔街见闻进行并行抓取的实验对比:Python多进程抓取 与 Java单线程和多线程抓取
相关参考:关于Python和Java的多进程多线程计算方法对比
6. 对于Ajax请求的处理
对于“加载更多”情况,使用Ajax来传输很多数据。
它的工作原理是:从网页的url加载网页的源代码之后,会在浏览器里执行JavaScript程序。这些程序会加载更多的内容,“填充”到网页里。这就是为什么如果你直接去爬网页本身的url,你会找不到页面的实际内容。
这里,若使用Google Chrome分析”请求“对应的链接(方法:右键→审查元素→Network→清空,点击”加载更多“,出现对应的GET链接寻找Type为text/html的,点击,查看get参数或者复制Request URL),循环过程。
如果“请求”之前有页面,依据上一步的网址进行分析推导第1页。以此类推,抓取抓Ajax地址的数据。
对返回的json格式数据(str)进行正则匹配。json格式数据中,需从’uxxxx’形式的unicode_escape编码转换成u’uxxxx’的unicode编码。
7. 自动化测试工具Selenium
Selenium是一款自动化测试工具。它能实现操纵浏览器,包括字符填充、鼠标点击、获取元素、页面切换等一系列操作。总之,凡是浏览器能做的事,Selenium都能够做到。
这里列出在给定城市列表后,使用selenium来动态抓取去哪儿网的票价信息的代码。
参考项目:网络爬虫之Selenium使用代理登陆:爬取去哪儿网站
8. 验证码识别
对于网站有验证码的情况,我们有三种办法:
使用代理,更新IP。
使用cookie登陆。
验证码识别。
使用代理和使用cookie登陆之前已经讲过,下面讲一下验证码识别。
可以利用开源的Tesseract-OCR系统进行验证码图片的下载及识别,将识别的字符传到爬虫系统进行模拟登陆。当然也可以将验证码图片上传到打码平台上进行识别。如果不成功,可以再次更新验证码识别,直到成功为止。
参考项目:Captcha1
爬取有两个需要注意的问题:
如何监控一系列网站的更新情况,也就是说,如何进行增量式爬取?
对于海量数据,如何实现分布式爬取?
分析
抓取之后就是对抓取的内容进行分析,你需要什么内容,就从中提炼出相关的内容来。
常见的分析工具有正则表达式,BeautifulSoup,lxml等等。
存储
分析出我们需要的内容之后,接下来就是存储了。
我们可以选择存入文本文件,也可以选择存入MySQL或MongoDB数据库等。
存储有两个需要注意的问题:
如何进行网页去重?
内容以什么形式存储?
Scrapy
Scrapy是一个基于Twisted的开源的Python爬虫框架,在工业中应用非常广泛
有很多开源的网络爬虫,如果我们掌握某一种或多种开源的爬虫工具,再我们获取数据的道路上会如虎添翼,事半功倍。这里我介绍一下我对于Scrapy网络爬虫的学习和搭建。
首先安装scrapy。在Windows和Linux下各有不同的办法,推荐在Linux下使用。
安装好了scrapy环境,我们就可以在Windows和Linux下使用这个爬虫框架了。
1、首先搭建工程:比如说我要建立一个工程,名字叫Wechatproject
输入命令:scrapy startproject Wechatproject(project名称)
那么生成的工程目录如下:
Wechatproject
├── Wechatproject
│ ├── __init__.py
│ ├── items.py
│ ├── pipelines.py
│ ├── settings.py
│ └── spiders
│ └── __init__.py
└── scrapy.cfg
scrapy.cfg 是整个项目的设置,主要用于部署scrapy的服务,一般不会涉及。
items.py 定义抓取结果中单个项所需要包含的所有内容。【目标】
settings.py 是scrapy的设置文件,可对其行为进行调整。【设置】
在配置文件中开启pipline插件,添加
ITEM_PIPELINES = [‘Wechatproject.pipelines.WechatprojectPipeline’] # add settings
如果需要下载images,添加
ITEM_PIPELINES = {‘Wechatproject.pipelines.WechatprojectPipeline':1, ‘Wechatproject.pipelines.MyImagesPipeline':2} # add settings
IMAGES_STORE = ‘./images’
pipelines.py 定义如何对抓取到的内容进行再处理,例如输出文件、写入数据库等。【处理】
只有一个需要实现的方法:process_item。如果输出文件、写入数据库等,需要设置settings.py文件。
如果需要下载images,添加:
”’if you want to download images”’
# from scrapy.http.request import Request
# from scrapy.contrib.pipeline.images import ImagesPipeline
# class MyImagesPipeline(ImagesPipeline):
# #@TODO
# def get_media_requests(self, item, info):
# for image_url in item[‘image_urls’]: # item[‘image_urls’] contains the image urls
# # yield Request(image_url)
# yield Request(image_url, meta={‘name': item[‘name’]}) # item[‘name’] contains the images name
# def item_completed(self, results, item, info):
# return super(MyImagesPipeline, self).item_completed(results, item, info)
# def file_path(self, request, response=None, info=None):
# f_path = super(MyImagesPipeline, self).file_path(request, response, info)
# f_path = f_path.replace(‘full’, request.meta[‘name’])
# return f_path
# ##########################################################
# # import hashlib
# # image_guid = hashlib.sha1(request.url).hexdigest() # change to request.url after deprecation
# # return ‘%s/%s.jpg’ % (request.meta[‘name’], image_guid)
# pass
# # from scrapy.contrib.pipeline.media import MediaPipeline
# # class MyMediaPipeline(MediaPipeline):
# # #@TODO
# # pass
spiders 目录下存放写好的spider,也即是实际抓取逻辑。【工具】
parse()方法可以返回两种值:BaseItem,或者Request。通过Request可以实现递归抓取(from scrapy.http import Request)。
如果要抓取的数据在当前页,可以直接解析返回item。
例如:yield item
如果要抓取的数据在当前页指向的页面,则返回Request并指定parse2作为callback。
如果要抓取的数据当前页有一部分,指向的页面有一部分.这种情况需要用Request的meta参数把当前页面解析到的数据传到parse2,后者继续解析item剩下的数据。
2、将来上述文件加入工程,便于文件管理
在Wechatproject工程目录下,新建Spider_Main.py,内容如下:
from scrapy.cmdline import execute
import sys
sys.argv = ["scrapy", "crawl", "wechat"]
execute()
3、在spider文件夹下定义spider。
spider定义三个主要的、强制的属性:
name【spider的标识】;
start_urls【一个需要爬取的链接起始列表】;
parse()【调用时传入每一个url传回的Response对象作为参数,Response是传入这个方法的唯一参数】。
Scrapy为爬虫的start_urls属性中的每个url创建了一个scrapy.http.Request对象,这些scrapy.http.Request首先被调度,然后被执行,之后通过爬虫的parse()方法作为回调函数,scapy.http.Response对象被返回,结果也被反馈给爬虫。
parse()方法是用来处理Response对象,返回爬取的数据,并且获得更多等待爬取的链接。
可以使用其他分析方法如正则表达式或者BeautifulSoup对response.body进行分析,不局限于xpath()方法。
from scrapy.selector import Selector
sel = Selector(response) # sel是一个selector
sel.xpath()返回selectors列表,每一个selector表示一个xpath参数表达式选择的节点这样就可以更快的获取需要的数据。
使用sel.xpath().extract()取出节点下面的文本数据或者使用sel.xpath().re(r”(w+)”)来正则匹配其中元素。
同理,
from scrapy.selector import HtmlXPathSelector
hxs = HtmlXPathSelector(response)
使用hxs.select()返回htmlxpathselectors列表,和hxs.select().extract()取出数据或者hxs.select().re(r”(w+)”)来正则匹配。
4、网络抓取:scrapy crawl wechat(spider名称) 或 scrapy crawl wechat -o resultsitems.json -t json
使用Scrapy或Requests递归抓取微信搜索结果
使用Scrapy方法 或者 使用Requests+BeautifulSoup
使用Scrapy方法:
将querystring替换为你要查询的单词
type可以选择
i的范围可以调整,对应查询的搜索结果页面数目1>熟悉python,基本上到了能够写一些小程序就够了,其他的不会可以边用边学。
2>熟悉使用跟爬虫相关的一些模块,比如urllib,urllib2,cookielib ,requests。
这里有一些必要的网络或者是web基础知识要知道,比如cookie是什么?网站的请求的get和post的区别?
3>熟悉使用网页处理的相关模块:re,BeautifulSoup。处理的时候可以BeautifulSoup缩小查找范围,之后使用re模块精确匹配你要的信息。
4>熟悉使用多线程。
5>熟悉使用各类文件(文本,excel)操作或者熟悉使用各类关系或非关系型数据库。
以上都会使用了基本上可以进行一些数据量不太大的数据抓取工作了。
但是如果想像楼上答主说的抓取整个豆瓣的话,应该还要学习更多的东西,比如爬虫框架,集群什么的,具体我现在还不了解。
注:抓取赶集网上担保公司和CSDN博客已经记录在我的博客了,后面抓取微博数据以及如何分析处理也会记录在博客中,欢迎浏览:python爬虫Pragmatic系列I
备份某问题所有答案:
import zhihu
question = zhihu.Question(‘http://www.zhihu.com/question/28092572′)
for answer in question.answers:
answer.save()
会在当前目录下新建以问题标题命名的文件夹,并将所有html文件保存到该文件夹。
save函数默认目录为当前目录下以问题标题开头的目录,默认文件名为问题标题加上答题者昵称,有相同昵称的情况下自动加上序号。
备份某用户所有答案:
import zhihu
author = zhihu.Author(‘http://www.zhihu.com/people/7sdream’)
for answer in author.answers:
# print(answer.question.title)
answer.save(path=author.name)
会在当前目录下新建以作者昵称命名的文件夹,并将所有html文件保存到该文件夹。
备份某收藏夹所有答案:
import zhihu
collection = zhihu.Collection(‘http://www.zhihu.com/collection/37770691′)
for answer in collection.answers:
# print(answer.question.title)
answer.save(path=collection.name)
会在当前目录下新建以收藏夹名称命名的文件夹,并将所有html文件保存到该文件夹。
1.页面下载程序;
最简单的工具就是urllib、urllib2。这两个工具可以实现基本的下载功能,如果进阶想要异步可以使用多线程,如果想效率更高采用非阻塞方案tornado和curl可以实现非阻塞的下载。
2.链接发掘程序;
要想在页面中找到新链接需要对页面解析和对url排重,正则和DOM都可以实现这个功能,看自己熟悉哪一种。
正则感觉速度较快一些,DOM相对较慢并且复杂一点,如果只是为了要url正则可以解决,如果还想要页面中其他的结构或者内容DOM比较方便。
url的排重两小可以用memcache或者redis,量大就要用到bloomfilter。
3.网页存储等部分组成;
抓的少怎么存都行,抓的多并且要方便读取那就要好好设计了,用哈希分布存储在RDBMS上或者直接存在HBase上都要看你的数据量和具体需求。
python可能就是因为有scrapy才成为最好学习的爬虫工具,不过在做爬虫之前有一点需要明确,爬虫要做到多大规模,因为这涉及到服务器的数量和存储的问题。其实说道服务器数量并不是说爬虫很需要计算量,而是大多数网站都有ip限制,比如同一个ip在一个小时之内访问不能超过1万次,或者更少,就算只用1核的电脑一个小时也肯定超过这个数了,所以爬虫之所以叫爬虫,就是要小但多~,每一台机器都有一个ip,用来突破ip限制(一定要确定每台机器都要有自己的公网ip,不能靠端口映射得到的ip,这样的ip还是会被限制,所以自己搭建爬虫集群变得几乎不可能,这个时候我们要用“云计算”)。二可能就是存储的问题了,当你MySQL一个表存了超过1亿行之后,别想MySQL还能正常工作,MongoDB也救不了你,通常的做法是分表分库,数据库集群等等。
如果小规模玩耍的话,那基本的html要会,因为要解析这些标签,需要程序员自己先看的懂。很多方法scrapy都封装了,除了使用现成的方法,还要试着理解其实现的原理。最后我觉得xml和json要了解,因为无论如何你肯定不想把html原样存储,html不仅体积巨大,冗余信息多,而且难以解读,python对xml和json支持都非常好,尤其是json与python的字典对应,还与mongodb的doc对应,所以将网页中有效的内容转化成JSON然后存进mongodb是一个不错的选择(我特别喜欢这种方式,在这种便利面前,MySQL简直不忍直视~)。当然爬虫只是数据业务的起步,如何有效的玩弄数据,挖掘数据,才是体现数据价值的地方~
我们先来看看,如果是人正常的行为,是如何获取网页内容的。
(1)打开浏览器,输入URL,打开源网页
(2)选取我们想要的内容,包括标题,作者,摘要,正文等信息
(3)存储到硬盘中
上面的三个过程,映射到技术层面上,其实就是:网络请求,抓取结构化数据,数据存储。
我们使用Python写一个简单的程序,实现上面的简单抓取功能。
import urllib2, re, cookielib
def httpCrawler(url):
”’
@summary: 网页抓取
”’
content = httpRequest(url)
title = parseHtml(content)
saveData(title)
def httpRequest(url):
”’
@summary: 网络请求
”’
try:
ret = None
SockFile = None
request = urllib2.Request(url)
request.add_header(‘User-Agent’, ‘Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322)’)
request.add_header(‘Pragma’, ‘no-cache’)
opener = urllib2.build_opener()
SockFile = opener.open(request)
ret = SockFile.read()
finally:
if SockFile:
SockFile.close()
return ret
def parseHtml(html):
”’
@summary: 抓取结构化数据
”’
content = None
pattern = ‘<title>([^<]*?)</title>’
temp = re.findall(pattern, html)
if temp:
content = temp[0]
return content
def saveData(data):
”’
@summary: 数据存储
”’
f = open(‘test’, ‘wb’)
f.write(data)
f.close()
if __name__ == ‘__main__':
url = ‘http://www.baidu.com’
httpCrawler(url)
看着是不是很简单,是的,其实上面就是实现了一个简单的抓取,也是入门爬虫最基础的抓取代码,采集软件就是基于这种原理,再扩展出各种需求,各种监控和反监控,各种资源调度,各种服务以及管理,可以移步到这里:Python简单爬虫引出分布式爬虫
Scrapy(Scrapy | A Fast and Powerful Scraping and Web Crawling Framework): 非常好用的爬虫框架。用它来解决单机多线程爬内容的过程非常舒服。不足之处是只支持Python 2,所以字符串有str和unicode两种类型,处理多语言内容和JSON数据需要多留意不同字符串的类型转换。学习Scrapy的过程中要尽力去了解xpath语法。
Google Chrome Developer Tools(https://developer.chrome.com/devtools):你没有看错,就是Chrome浏览器自带的Developer Tools,任何页面按Cntrl + Shift + I就可以打开的那个丑陋的界面。你想问它有什么用?答案是追踪页面资源。现代网页有许多的AJAX内容,需要页面打开的时候自动加载。其原理大都是页面在浏览器里面去进行更多的HTTP(S)请求来获取对应的JSON数据。这些请求除非爬虫程序有某种JavaScript执行引擎,否则爬虫是拿不到数据的。这个时候其实完全可以手工打开Developer Tools的网络(Network)页面,然后刷新页面看哪些资源在页面加载之后被访问。通过搜索很容易就得到获取这些JSON数据的请求地址的规律,然后用Scrapy直接爬,没必要用JavaScript引擎去执行页面。爬虫工程师一定要记住这句话:多找规律,少看代码!
Redis(Redis):当需要并行抓取的时候,就需要某些数据库来对集群内不同的机器进行同步。我个人比较喜欢用Redis,因为它提供了很多高阶分布式数据结构,避免自己写很多机器之间进行同步的代码。
云服务器及其API(如Amazon EC2):这主要是考虑到很多网站有反爬虫的功能,需要我们能够自动地切换爬虫器IP。一个例子是上个月在Amazon EC2上开了40台nano云服务器抓韩国11街的用户评论,但是它总是过十几个小时就会封IP,三天解封。这个时候只要把对应的云服务器关机,半个小时之后重新开机就会有新的公网IP。打一炮就换个地儿嘛!
v1: Python2.7.5
爬豆瓣日记,下载整个html
某次调用频率太高,被error403。StackOberflow的解决办法是模拟浏览器登录,尝试了下,没有解决。在SegmentFault上某位大大建议爬API,遂改进
v2: Python 3.4.0
重构,仍然豆瓣,爬API,引入了正则表达式,代码减少
基本不会被error403
回到题目,「Python爬虫入门」
假设你目前的编程能力是「能够用C写简单的循环」
那么完成这个简单的爬虫v1,你至少需要具备:
不太糟糕的英语阅读能力,Python的语法非常简单
基本的字符串操作
html基本的语法,你只需要看懂几个关键词
#Python3.x 和 Python2.x的字符编码方式稍有区别,适当注意一下版本
#我也是菜鸟,有问题可以私信我
发布于 2014-04-19 6 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利
10
赞同反对,不会显示你的姓名
daoluan 挨踢人士
10 人赞同
Python 内置了简单的爬虫工具:urllib/urllib2,自动化程度没那么高。想学当然是看官方的文档:20.5. urllib / 20.6. urllib2
简单的爬虫工作过程是:
模拟发送一个 HTTP 请求,获取回复内容,一般是 HTML 代码;
用正则表达式获取目标内容
当然,这是最简单的爬虫工作了,如果你对「如何模拟一个 HTTP 请求以及发送过程」,可以参考之前写下的关于 Python urllib/urllib2 的源码阅读笔记:urllib 源码小剖 和 urllib2 源码小剖(urllib2 自我感觉思想不错)。
一、网络爬虫的定义
网络爬虫,即Web Spider,是一个很形象的名字。
把互联网比喻成一个蜘蛛网,那么Spider就是在网上爬来爬去的蜘蛛。
网络蜘蛛是通过网页的链接地址来寻找网页的。
从网站某一个页面(通常是首页)开始,读取网页的内容,找到在网页中的其它链接地址,
然后通过这些链接地址寻找下一个网页,这样一直循环下去,直到把这个网站所有的网页都抓取完为止。
如果把整个互联网当成一个网站,那么网络蜘蛛就可以用这个原理把互联网上所有的网页都抓取下来。
这样看来,网络爬虫就是一个爬行程序,一个抓取网页的程序。
网络爬虫的基本操作是抓取网页。
那么如何才能随心所欲地获得自己想要的页面?
我们先从URL开始。
二、浏览网页的过程
抓取网页的过程其实和读者平时使用IE浏览器浏览网页的道理是一样的。
比如说你在浏览器的地址栏中输入 http://www.baidu.com/ 这个地址。
打开网页的过程其实就是浏览器作为一个浏览的“客户端”,向服务器端发送了 一次请求,把服务器端的文件“抓”到本地,再进行解释、展现。
HTML是一种标记语言,用标签标记内容并加以解析和区分。
浏览器的功能是将获取到的HTML代码进行解析,然后将原始的代码转变成我们直接看到的网站页面。
三、URI和URL的概念和举例
简单的来讲,URL就是在浏览器端输入的 http://www.baidu.com/ 这个字符串。
在理解URL之前,首先要理解URI的概念。
什么是URI?
Web上每种可用的资源,如 HTML文档、图像、视频片段、程序等都由一个通用资源标志符(Universal Resource Identifier, URI)进行定位。
URI通常由三部分组成:
①访问资源的命名机制;
②存放资源的主机名;
③资源自身 的名称,由路径表示。
四、URL的理解和举例
URL是URI的一个子集。它是Uniform Resource Locator的缩写,译为“统一资源定位 符”。
通俗地说,URL是Internet上描述信息资源的字符串,主要用在各种WWW客户程序和服务器程序上。
采用URL可以用一种统一的格式来描述各种信息资源,包括文件、服务器的地址和目录等。
URL的一般格式为(带方括号[]的为可选项):
protocol :// hostname[:port] / path / [;parameters][?query]#fragment
URL的格式由三部分组成:
①第一部分是协议(或称为服务方式)。
②第二部分是存有该资源的主机IP地址(有时也包括端口号)。
③第三部分是主机资源的具体地址,如目录和文件名等。
第一部分和第二部分用“://”符号隔开,
第二部分和第三部分用“/”符号隔开。
第一部分和第二部分是不可缺少的,第三部分有时可以省略。
五、URL和URI简单比较
URI属于URL更低层次的抽象,一种字符串文本标准。
换句话说,URI属于父类,而URL属于URI的子类。URL是URI的一个子集。
URI的定义是:统一资源标识符;
URL的定义是:统一资源定位符。
二者的区别在于,URI表示请求服务器的路径,定义这么一个资源。
而URL同时说明要如何访问这个资源(http://)。
前段时间,我看见腾讯课堂上有关于网络爬虫的课程,大家可以去看看,我看了一次,讲得还不错,也很咨询。python项目实战
0. 学习动机
有个很喜欢的韩国服装模特,想把她的网站민영언니의 로맨틱 시크 고급스러운 데일리룩 민티크 ♥上的套图都下载下来,之前用过wget -r -np -nd的形式,但下载下来的有很多杂图,于是想要更准确地得到所有服装图片,便打算学习一下网上有很多资料的python爬虫。
1. 图片批量下载
urllib和re模块是爬虫最基本的东西,urllib用来获取网页,而re用来从网页中获得信息。
urllib主要可能用到urlopen和urlretrieve函数,分别用来打开网页和下载资源。
re模块就是学会正则表达式。
之后看一下网页源码就能很容易地写出下载套图的脚本了:Trash/test01.py at master · Sunkworld/Trash · GitHub
2. 网关连接的Alfred Workflow
之前在bbs上看到过有人求学校网关连接的Alfred Workflow,学了一点python之后就打算写个网关登陆的脚本整合到Alfred里,这样自己用也方便。
刚开始用urllib和urllib2将信息传递给网关连接,并用urllib2.HTTPCookieProcessor()保持登陆状态,做了个最基本的脚本。
之后知道了有更好的接口,便换了接口,并用正则抓取网页得到当前网关的信息,整合到Alfred Workflow里推送提醒。
bbs上有人提出能否实现定时断开收费地址,在搜索调试了一段时间后,用subprocess模块+一句话的shell脚本骗过了Alfred的运行机制。
最终将所有东西整合到Alfred Workflow中:Trash/pku-ipgw-workflow at master · Sunkworld/Trash · GitHub
于是现在连接网关我都只要用Alfred敲几个键就好,也再也不用担心忘关收费地址了。
3. 刷课脚本
开学之后体育课忘记投点然后掉了,于是很不开心地开始写了刷课的脚本。
一开始用urllib+HTTPCookieProcessor写了一个简陋的脚本,重复登陆来检查选课人数是否改变,当天跑了1600多次之后选课网挂了,不敢再用重新登陆的办法,于是用保持登陆状态并点击刷新按钮的办法来刷课,同时抛弃了urllib转投requests。
之后觉得这样太简陋了都不会自动选课,就测试了下验证码的检验方法,将验证码下载到本地手动输入后便能在刷到空位时自动选上课。
后面几天选课网的网络状态一直不太好,而重新连接的话验证码就会失效,便决定将整个脚本重写得更有逻辑些,加上网络状态的处理和验证码的自动识别,验证码的自动识别用了网上现成的一个去噪脚本和tesseract,因为选课系统验证码简单,基本两三次就能识别成功。
但由于两次点击刷新按钮间要间隔5s,课一多刷一轮下来就要很久,便学习了一下开多线程的办法,然后经过测试发现开6个线程之后网络就会开始出现问题,开17个线程之后所有线程全会死掉,于是把线程数固定在了6个。【网上有个chrome插件似乎能同时刷新几十门课我到现在也不知道它是怎么做的orz
最终版的刷课脚本就不需要人工干涉了:Trash/elective at master · Sunkworld/Trash · GitHub
然后就把我的脚本放到了vps上用screen挂着全天跑,帮同学刷上通选课和体育课过,然而我自己仍然没刷上体育课orz…
4. 定时查看微博
之前一直听说BeautifulSoup但似乎一直没用到,便打算写个用得到它的小脚本,又正好想写个定时查看某人是否发了新微博的脚本。
刚开始想爬http://weibo.com然后发现啥都得不到,搜了下才知道反爬虫机制,于是从http://weibo.cn入手,由于登陆需要输验证码便用cookie登陆。
之后发邮件给自己这个任务折腾了很久,终端下的邮件客户端不会配,smtplib在本地工作地很好但放到vps上就哪都连不上,最后勉强发现gmail的587端口能连上便在vps上用gmail做了。
BeautifulSoup相比re模块似乎容易写一些,但感觉命令太多都记不住,但大概还是写出了能用的代码:Trash/auto-check-weibo at master · Sunkworld/Trash · GitHub
脚本放到vps上之后一直报错,后来发现似乎是没安装python-lxml的锅。然后写完之后发现这不就是个IFTTT…虽然那货似乎没有微博的接口。
5. 自动转存新番
这季追的新番上了两位数,懒得看更新或者手动下载,又考虑到有d站的存在,就尝试写了个把新番自动转存到自己的百度云然后同步盘同步下来的脚本。
先是获取整个d站的资源列表,由于没有并发数限制,可以开一堆线程操作。
然后就是百度云的转存问题,抓包感觉递交的表单太复杂,api又不了解,便决定用selenium直接模拟浏览器操作,由于要放到服务器上跑,浏览器便用了phantomjs。登录方面使用的是通过selenium和requests的结合,传递session的cookie,将验证码下载到本地之后再用tesseract解析
在百姓网爬对手数据—赶集招聘。
内部的需求方需要赶集招聘的28个字段数据。大致是:发布公司、职位名称、电话号码一类的。
这些数据有两种:
1.静态,http请求接受就有的
2.动态,http请求完,需要在浏览器中执行js动态ajax请求的
我需要爬取的数据中,26个数据都是静态的,只有2个数据是动态的。
Nice!静态数据用requests+xpath获得,print一下完全正确。
动态数据用selenium+xpath去拿,print一下也是对的。
数据拿到了,接着存储、去重。
这就需要架构了…
第一遍用 urllib2 , re 等标准 module 来写
然后可以尝试用 requests,bs4 等第三方 library 写,体会这些模块带来的优势和劣势
不建议用 scrapy 这些框架,对新手学习 Python 不是很有帮助。
入门是一件很简单的事情,如果你已经学习了Python的基本语法,那么就可以开始写一些小爬虫了。
Python入门书籍选择:
《Python简明教程》、
《Learn Python The Hard Way》
Python爬虫入门书籍
《Web Scraping With Python》
正则表达式入门
《正则表达式30分钟入门教程》
Pyhon进阶书籍
《Python Cookbook》
开始写爬虫前
1. 先学习一下计算机网络,知道一些基本的网络知识,比如说域名、get和post的区别、HTTP响应报文等
2. 先学习一下简单的正则表达式,除了常见的? + *,还需要掌握断言和分组,这些在爬虫里经常会被使用
1. 入门
首先学会最基本的urllib模块和re模块,选择简单的图片或小说网站上手,只需要简单的几行代码就可以爬取一些你感兴趣的图片或者小说。
然后你就入门了,这时候的你应该学会了基本的urlopen(), read(),简单的文件操作write(),还有匹配图片的正则表达式。
2. 再入门
然后你就可以进行数量大一点的爬取了,仍然是那些简单的网站但是这次你可以爬取多个页面,然后将图片或小说分类保存于不同目录。
这时候的你应该学会了常见异常的处理,比如连接超时异常,文件路径异常。
3. 进阶
接下来可以考虑爬一些需要登录的网站,比如你正在使用的知乎,或者豆瓣。这次仍然是爬取一些图片或者文字。
这时候的你应该学会了简单的headers和request()。你也可以使用一些进阶模块,比如requests, BeautifulSoup。学会post()和get()来处理网站的登录与获取,另外学会用BeautifulSoup来处理网页。除此之外你应该已经下载了pip,这样以后安装其他Python模块时会方便很多。
4. 再进阶
然后你可以尝试写大一点的爬虫,比如写一个每天更新你关注的知乎收藏夹的爬虫,或者爬取豆瓣上的更新。
5. 。。。
做到以上你应该就可以写出简单的满足日常需求的爬虫了。如果你对爬虫仍然有兴趣的话可以试一下接下来的。大规模爬取并对爬取的数据做一些你感兴趣的方向的分析,然后你就可以学习高级模块,接触队列、哈希表等数据结构,接触BFS,DFS等算法,接触多线程、多进程、死锁等操作系统知识,接触数据库的操作,来写出更加高效的爬虫并对爬取的内容进行更好的管理与维护。
你甚至可以学习一些数学软件、图像软件来对数据进行建模分析并将结果可视化。继续深入的话还有好多好多,总之越往后学你会发现世界就会越大,
运行第一个爬虫程序。
自动下载某页面下的图片到程序目录下。
所用库:urllib,re(正则表达式)
#coding=utf-8
import urllib
import re
def getHtml(url):
page = urllib.urlopen(url)
html = page.read()
return html
def getImg(html):
reg = r’src="(.+?.jpg)" pic_ext’
imgre = re.compile(reg)
imglist = re.findall(imgre,html)
x = 0
for imgurl in imglist:
urllib.urlretrieve(imgurl,’%s.jpg’ % x)
x+=1
html = getHtml("http://tieba.baidu.com/p/2558403642"
print getImg(html)
运行结果。
既然已经了解到有这两个库,那么就沿着这条线看下去。
第一个urllib的函数使用
读取某页面第一行html
用到urllib下的urlopen函数。
import urllib
f = urllib.urlopen(‘http://www.baidu.com’)
firstLine = f.readline() #读取html页面的第一行
print firstLine
很简单,使用也很明确。
运行效果
re.match:匹配字符串开始是否与希望匹配的东西相同。
函数原型:re.match(pattern, string, flags)
例:
import re
text = "JGood is a handsome boy, he is cool, clever, and so on…"
m = re.match(r"(w+)s", text)
if m:
print m.group(0), ‘n’, m.group(1)
else:
print ‘not match’
运行结果
re.search:
函数原型:re.search(pattern, string, flags)
例:
import re
text = "JGood is a handsome boy, he is cool, clever, and so on…"
m = re.search(r’shan(ds)omes’, text)
if m:
print m.group(0), m.group(1)
else:
print ‘not search’
运行结果:
re.sub:
函数原型:re.sub(pattern, repl, string, count)
例:
import re
text = "JGood is a handsome boy, he is cool, clever, and so on…"
print re.sub(r’s+’, ‘-‘, text)
将空格都替换成-
运行结果:
以及:
re.split
可以使用re.split来分割字符串,如:re.split(r’s+’, text);将字符串按空格分割成一个单词列表。
re.findall
re.findall可以获取字符串中所有匹配的字符串。如:re.findall(r’w*oow*’, text);获取字符串中,包含’oo’的所有单词。
re.compile
可以把正则表达式编译成一个正则表达式对象。可以把那些经常使用的正则表达式编译成正则表达式对象,这样可以提高一定的效率。