爬虫在平时也经常用,但一直没有系统的总结过,其实它涉及了许多的知识点。这一系列会理一遍这些知识点,不求详尽,只希望以点带面构建一个爬虫的知识框架。这一篇是概念性解释以及入门级爬虫介绍(以爬取网易新闻为例)。

爬虫基础

什么是爬虫

爬虫说白了其实就是获取资源的程序。制作爬虫的总体分三步:爬-取-存。首先要获取整个网页的所有内容,然后再取出其中对你有用的部分,最后再保存有用的部分。

爬虫类型

  • 网络爬虫
    网络爬虫,是一种按照一定的规则,自动的 抓取万维网信息的程序或者脚本。网络爬虫是搜索引擎系统中十分重要的组成部分,爬取的网页信息用于建立索引从而为搜索引擎提供支持,它决定着整个引擎系统的内容是否丰富,信息是否即时,其性能的优劣直接影响着搜索引擎的效果。
  • 传统爬虫
    从一个或若干初始网页的URL开始,获得初始网页的URL,在抓取网页过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。

工作原理

  • 根据一定的网页分析算法过滤与主题无关的链接,保留有用链接并将其放入等待抓取的URL队列
  • 根据一定的搜索策略从队列中选择下一步要抓取的网页URL,重复上述过程,直到达到指定条件才结束爬取
  • 对所有抓取的网页进行一定的分析、过滤,并建立索引,以便之后的查询和检索。

爬取策略

广度优先

完成当前层次的搜索后才进行下一层次的搜索。一般的使用策略,一般通过队列来实现。

最佳优先

会有评估算法,凡是被算法评估为有用的网页,先来爬取。

深度优先

实际应用很少。可能会导致trapped问题。通过栈来实现。

URL( Uniform Resource Locator: 统一资源定位符)

互联网上资源均有其唯一的地址,由三部分组成。

  • 模式/协议
  • 文件所在IP地址及端口号
  • 主机上的资源位置
  • 例子:http://www.example.com/index.html

Web Server/Socket如何建立连接和传输数据的

web server 的工作过程其实和打电话的过程差不多(买电话–>注册号码–>监听–>排队接听–>读写–>关闭),经典的三步握手(有人在吗?我在呢,你呢?我也在)在排队接听时进行。下面一张图足以解释一切。

Crawler端需要一个socket接口,向服务器端发起connect请求,完成连接后就可以和服务器交流了,操作完毕会关闭socket接口。服务器端更复杂一点,也需要一个socket接口,并且这个socket接口需要绑定一个地址(bind()),这就相当于有一个固定的电话号码,这样其他人拨打这个号码就可以找到这个服务器。绑定之后服务器的socket就开始监听(listen())有没有用户请求,如果有,就接收请求(accept()),和用户建立连接,然后就可以交流。

HTML DOM

  • DOM 将 HTML 文档表达为树结构
  • 定义了访问和操作 HTML 文档的标准

Cookie

  • 由服务器端生成,发送给 User-Agent(一般是浏览器),浏览器会将 Cookie 的 key/value 保存到某个目录下的文本文件哪,下次访问同一网站时就发送该 Cookie 给服务器。

HTTP

  • GET 直接以链接形式访问,链接中包含了所有的参数
  • PUT 把提交的数据放到 HTTP 包的包体中
    eg.
    import urllib
    import urllib2
    url='http://www.zhihu.com/#signin'
    user_agent='MOZILLA/5.0'
    values={'username':'252618408@qq.com','password':'xxx'}
    headers={'User-Agent':user_agent}
    data=urllib.urlencode(values) # urlencode 是 urllib 独有的方法
    request=urllib2.Request(url,data,headers) # write a letter
    response=urllib2.urlopen(request) # send the letter and get the reply
    page=response.read() # read the reply
    

urllib 仅可以接受 URL,这意味着你不可以伪装你的 User Agent 字符串等,但 urllib 提供了 urlencode 方法用来GET查询字符串等产生,而 urllib2 没有。
因此 urllib, urllib2经常一起使用。

Headers 设置

  • User-Agent: 部分服务器或 Proxy 会通过该值来判断是否是浏览器发出的请求
  • Content-Type: 使用 REST 接口时,服务器会检查该值,用来确定 HTTP Body 中的内容该怎样解析
  • application/xml: 在 XMl RPC, 如 RESTful/SOAP 调用时使用
  • application/json: 在 JSON RPC 调用时使用
  • application/x-www-form-urlencoded: 浏览器提交 Web 表单时使用

爬虫难点

爬虫的两部分,一是下载 Web 页面,有许多问题需要考虑。如何最大程度地利用本地带宽,如何调度针对不同站点的 Web 请求以减轻对方服务器的负担等。一个高性能的 Web Crawler 系统里,DNS 查询也会成为急需优化的瓶颈,另外,还有一些“行规”需要遵循(例如robots.txt)。
而获取了网页之后的分析过程也是非常复杂的,Internet 上的东西千奇百怪,各种错误百出的 HTML 页面都有,要想全部分析清楚几乎是不可能的事;另外,随着 AJAX 的流行,如何获取由 Javascript 动态生成的内容成了一大难题;除此之外,Internet 上还有有各种有意或无意出现的 Spider Trap ,如果盲目的跟踪超链接的话,就会陷入 Trap 中万劫不复了,例如这个网站,据说是之前 Google 宣称 Internet 上的 Unique URL 数目已经达到了 1 trillion 个,因此这个人 is proud to announce the second trillion 。

最简单的爬虫

requests 库

import requests
url = "http://shuang0420.github.io/"
r = requests.get(url)

urllib2 库

1
2
3
4
5
6
7
8
9
10
11
import urllib2
# request source file
url = "http://shuang0420.github.io/"
request = urllib2.Request(url) # write a letter
response = urllib2.urlopen(request) # send the letter and get the reply
page = response.read() # read the reply
# save source file
webFile = open('webPage.html', 'wb')
webFile.write(page)
webFile.close()

这是一个简单的爬虫,打开 webPage.html 是这样的显示,没有css.

实例:爬取网易新闻

爬取网易新闻 [代码示例]
– 使用 urllib2 的 requests包来爬取页面
– 使用正则表达式和 bs4 分析一级页面,使用 Xpath 来分析二级页面
– 将得到的标题和链接,保存为本地文件

分析初始页面

我们的初始页面是 http://news.163.com/rank

查看源代码

我们想要的是分类标题和URL,需要解析 DOM 文档树,这里使用了 BeautifulSoup 里的方法。

1
2
3
4
5
6
7
8
9
10
11
12
def Nav_Info(myPage):
# 二级导航的标题和页面
pageInfo = re.findall(r'<div class="subNav">.*?<div class="area areabg1">', myPage, re.S)[
0].replace('<div class="subNav">', '').replace('<div class="area areabg1">', '')
soup = BeautifulSoup(pageInfo, "lxml")
tags = soup('a')
topics = []
for tag in tags:
# 只要 科技、财经、体育 的新闻
# if (tag.string=='科技' or tag.string=='财经' or tag.string=='体育'):
topics.append((tag.string, tag.get('href', None)))
return topics

然而,Beautiful Soup对文档的解析速度不会比它所依赖的解析器更快,如果对计算时间要求很高或者计算机的时间比程序员的时间更值钱,那么就应该直接使用 lxml。换句话说,还有提高Beautiful Soup效率的办法,使用lxml作为解析器。Beautiful Soup用lxml做解析器比用html5lib或Python内置解析器速度快很多。bs4 的默认解析器是 html.parser,使用lxml的代码如下:

BeautifulSoup(markup, "lxml")

分析二级页面

查看源代码

我们要爬取的是之间的新闻标题和链接,同样需要解析文档树,可以通过以下代码实现,这里用了 lxml 解析器,效率更高。

1
2
3
4
5
6
def News_Info(newPage):
# xpath 使用路径表达式来选取文档中的节点或节点集
dom = etree.HTML(newPage)
news_titles = dom.xpath('//tr/td/a/text()')
news_urls = dom.xpath('//tr/td/a/@href')
return zip(news_titles, news_urls)

完整代码

潜在问题

  1. 我们的任务是爬取1万个网页,按上面这个程序,耗费时间长,我们可以考虑开启多个线程(池)去一起爬取,或者用分布式架构去并发的爬取网页。

  2. 种子URL和后续解析到的URL都放在一个列表里,我们应该设计一个更合理的数据结构来存放这些待爬取的URL才是,比如队列或者优先队列。

  3. 对各个网站的url,我们一视同仁,事实上,我们应当区别对待。大站好站优先原则应当予以考虑。

  4. 每次发起请求,我们都是根据url发起请求,而这个过程中会牵涉到DNS解析,将url转换成ip地址。一个网站通常由成千上万的URL,因此,我们可以考虑将这些网站域名的IP地址进行缓存,避免每次都发起DNS请求,费时费力。

  5. 解析到网页中的urls后,我们没有做任何去重处理,全部放入待爬取的列表中。事实上,可能有很多链接是重复的,我们做了很多重复劳动。

  6. 爬虫被封禁问题

优化方案

  1. 并行爬取问题

    关于并行爬取,首先我们想到的是多线程或者线程池方式,一个爬虫程序内部开启多个线程。同一台机器开启多个爬虫程序,这样,我们就有N多爬取线程在同时工作,大大提高了效率。

    当然,如果我们要爬取的任务特别多,一台机器、一个网点肯定是不够的,我们必须考虑分布式爬虫。分布式架构,考虑的问题有很多,我们需要一个scheduler来分配任务并排序,各个爬虫之间还需要通信合作,共同完成任务,不要重复爬取相同的网页。分配任务时我们还需要考虑负载均衡以做到公平。(可以通过Hash,比如根据网站域名进行hash)

    负载均衡分派完任务之后,千万不要以为万事大吉了,万一哪台机器挂了呢?原先指派给挂掉的哪台机器的任务指派给谁?又或者哪天要增加几台机器,任务有该如何进行重新分配呢?所以我们还要 task table 来纪录状态。

  2. 待爬取网页队列
    如何对待待抓取队列,跟操作系统如何调度进程是类似的场景。
    不同网站,重要程度不同,因此,可以设计一个优先级队列来存放待爬起的网页链接。如此一来,每次抓取时,我们都优先爬取重要的网页。
    当然,你也可以效仿操作系统的进程调度策略之多级反馈队列调度算法。

  3. DNS缓存
    为了避免每次都发起DNS查询,我们可以将DNS进行缓存。DNS缓存当然是设计一个hash表来存储已有的域名及其IP。

  4. 网页去重
    说到网页去重,第一个想到的是垃圾邮件过滤。垃圾邮件过滤一个经典的解决方案是Bloom Filter(布隆过滤器)。布隆过滤器原理简单来说就是:建立一个大的位数组,然后用多个Hash函数对同一个url进行hash得到多个数字,然后将位数组中这些数字对应的位置为1。下次再来一个url时,同样是用多个Hash函数进行hash,得到多个数字,我们只需要判断位数组中这些数字对应的为是全为1,如果全为1,那么说明这个url已经出现过。如此,便完成了url去重的问题。当然,这种方法会有误差,只要误差在我们的容忍范围之类,比如1万个网页,我只爬取到了9999个,并不会有太大的实际影响。
    一种很不错的方法来自url相似度计算,简单介绍下。
    考虑到url本身的结构,对其相似度的计算就可以抽象为对其关键特征相似度的计算。比如可以把站点抽象为一维特征,目录深度抽象为一维特征,一级目录、二级目录、尾部页面的名字也都可以抽象为一维特征。比如下面两个url:
    url1: http://www.spongeliu.com/go/happy/1234.html
    url2: http://www.spongeliu.com/snoopy/tree/abcd.html

    特征:

    • 站点特征:如果两个url站点一样,则特征取值1,否则取值0;
    • 目录深度特征:特征取值分别是两个url的目录深度是否一致;
    • 一级目录特征:在这维特征的取值上,可以采用多种方法,比如如果一级目录名字相同则特征取1,否则取0;或者根据目录名字的编辑距离算出一个特征值;或者根据目录名字的pattern,如是否数字、是否字母、是否字母数字穿插等。这取决于具体需求,这里示例仅仅根据目录名是否相同取1和0;
    • 尾页面特征:这维特征的取值同一级目录,可以判断后缀是否相同、是否数字页、是否机器生成的随机字符串或者根据编辑长度来取值,具体也依赖于需求。这里示例仅仅判断最后一级目录的特征是否一致(比如是否都由数字组成、是否都有字母组成等)。

    这样,对于这两个url就获得了4个维度的特征,分别是:1 1 0 0 。有了这两个特征组合,就可以根据具体需求判断是否相似了。我们定义一下每个特征的重要程度,给出一个公式:

    similarity = feather1 * x1 + feather2*x2 + feather3*x3 + feather4*x4

    其中x表示对应特征的重要程度,比如我认为站点和目录都不重要,最后尾页面的特征才是最重要的,那么x1,x2,x3都可以取值为0,x4取值为1,这样根据similarity就能得出是否相似了。或者认为站点的重要性占10%,目录深度占50%,尾页面的特征占40%,那么系数分别取值为0.1\0.5\0\0.4即可。

    其实这样找出需要的特征,可以把这个问题简化成一个机器学习的问题,只需要人为判断出一批url是否相似,用svm训练一下就可以达到机器判断的目的。
    除了上面这种两个url相似度的判断,也可以将每一条url都抽象成一组特征,然后计算出一个url的得分,设置一个分数差的阈值,就可以达到从一大堆url中找出相似的url的目的。

  5. 数据存储的问题
    数据存储同样是个很有技术含量的问题。用关系数据库存取还是用NoSQL,抑或是自己设计特定的文件格式进行存储,都大有文章可做。

  6. 进程间通信
    分布式爬虫,就必然离不开进程间的通信。我们可以以规定的数据格式进行数据交互,完成进程间通信。

  7. 反爬虫机制问题
    针对反爬虫机制,我们可以通过轮换IP地址、轮换Cookie、修改用户代理(User Agent)、限制速度、避免重复性爬行模式等方法解决。

参考链接:

网络爬虫基本原理(一)
http://www.chinahadoop.cn/course/596/learn#lesson/11986
https://www.bittiger.io/blog/post/5pDTFcDwkmCvvmKys
https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html

原文地址: http://www.shuang0420.com/2016/06/11/%E7%88%AC%E8%99%AB%E6%80%BB%E7%BB%93%EF%BC%88%E4%B8%80%EF%BC%89/

爬虫总结(一)-- 爬虫基础 python实现相关推荐

  1. python网络爬虫的基本步骤-python爬虫入门需要哪些基础/python 网络爬虫教程

    如何入门 Python 爬虫 入门个吊,放弃 python爬虫入门需要哪些基础 现在之所以有多的小伙伴热衷于爬虫技术,无外乎是因为爬我们做很多事情,比如搜索引擎.采集数据.广告过滤等,以Python为 ...

  2. python爬虫视频 下载 黑马_Python爬虫能爬视频么(python爬虫零基础视频教程)

    Python爬虫能爬视频么 他视频没有的,但是跑了之后你要处理这个视频,就是问题的?你只需要,得到视频的播放地址,还是要把视频完整的下载到本地的.如果只是爬那个视频的下载地址,很简单,如果要下载的话, ...

  3. 要学会Python爬虫都需要什么基础呢?

    提到Python自然就会想到爬虫,很多同学学习Python的目的也是为了使用爬虫,那么你知道想要学会Python爬虫都需要什么基础吗?下面小千就来告诉你. 学Python爬虫需要掌握的基础 1.Pyt ...

  4. python爬虫知识大全_Python爬虫入门有哪些基础知识点

    1.什么是爬虫 爬虫,即网络爬虫,大家可以理解为在网络上爬行的一直蜘蛛,互联网就比作一张大网,而爬虫便是在这张网上爬来爬去的蜘蛛咯,如果它遇到资源,那么它就会抓取下来.想抓取什么?这个由你来控制它咯. ...

  5. python爬虫ppt_完全零基础 轻松学Python:数据类型:数字类型、空类型、布尔类型...

    Tip:点击上方或此处 "#完全零基础 轻松学Python" 话题,查看更多内容,欢迎点赞.分享.收藏,谢谢支持! 内 容 概 要 1. 数据类型概述 数据类型的概念.常见的八种数 ...

  6. python爬虫篇,零基础学爬虫之精华版

    爬虫简介 网络爬虫 爬虫指在使用程序模拟浏览器向服务端发出网络请求,以便获取服务端返回的内容. 但这些内容可能涉及到一些机密信息,所以爬虫领域目前来讲是属于灰色领域,切勿违法犯罪. 很多人学习pyth ...

  7. 慕课网python零基础入门教程_零基础Python爬虫入门学习一之综述

    原标题:零基础Python爬虫入门学习一之综述 大家好哈,最近博主在学习Python,学习期间也遇到一些问题,获得了一些经验,在此将自己的学习系统地整理下来,如果大家有兴趣学习爬虫的话,可以将这些文章 ...

  8. Python|http|Chrome Developer Tools|Postman|HTTPie|builtwith库|python-whois库|爬虫及解析|语言基础50课:学习(10)

    文章目录 系列目录 原项目地址 第31课:网络数据采集概述 爬虫的应用领域 爬虫合法性探讨 Robots协议 超文本传输协议(HTTP) 相关工具(Chrome Developer Tools.Pos ...

  9. 【最全笔记】基础Python爬虫入门全笔记

    第一章 网络爬虫之规则 一.Requests库入门 request库:http://www.python-requests.org 安装方法:pip install requests 抓取百度 imp ...

最新文章

  1. 用C语言解“爬动的蠕虫”题
  2. vsftp中配置默认目录
  3. Android RuntimePermissions运行时权限:单个运行时权限申请简例
  4. java网络通信:异步非阻塞I/O (NIO)
  5. Tornado的同步API写法举例实现GET/POST/DELETE请求+Tornado获取post请求中的json数据(转载)
  6. 域服务器2008系统密码忘记了,如何在Server 2008 R2上重置您忘记的域管理员密码 | MOS86...
  7. poj 1733 ParityGame 并查集 离散化
  8. 计算机与打印机整体方案,打印机的一些使用方案
  9. java 应用监控系统_GitHub - jiangbin216/JavaMonitor: 一款适用于Java应用的在线性能监控系统(JM)...
  10. 边工作边刷题:70天一遍leetcode: day 45-1
  11. 3.啊哈!算法 --- 一大波数正在靠近——枚举!很暴力
  12. idea database mysql驱动
  13. 《网络是怎样连接的》笔记
  14. Android ExoPlayer播放音视频的使用指南
  15. 天猫商城多幅图片并排展示广告效果,鼠标指向高亮其它阴影
  16. winform chart 网格线设置
  17. 常用的curl命令及参数详解
  18. Unity输入控制物体旋转和延迟缩放-课程作业
  19. 原创西门子SMART 200 modbus rtu通讯宇电温控器例程
  20. Ambarella SDK build 步骤解析

热门文章

  1. 【tool】测试用例检查表范例
  2. grep -R 关键字 目录
  3. 大话设计模式读书笔记11----建造者模式(Builder)
  4. openmp使用经验
  5. Linux下c和cuda混合编译,并生成动态链接库.so和使用
  6. shell脚本中一些特殊符号
  7. 基于fdatool的滤波器设计(低通、带通、高通)
  8. java 下载url图片_java下载url图片链接
  9. 【专利】检索网站到底哪个能用?
  10. [云炬ThinkPython阅读笔记]2.6 字符串运算