爬虫入门基础

Day01

一、爬虫介绍

什么是爬虫

爬虫:网络爬虫又称为网络蜘蛛,网络蚂蚁,网络机器人等,可以自动化浏览网络中的信息,当然浏览信息的时候需要按照我们的规定的规则进行,这些规则称之为网络爬虫算法,使用python可以很方便的写出爬虫程序,进行互联网信息的自动化检索

网 : 互联网
蜘蛛网: 互联网理解为蜘蛛网
爬虫: 蜘蛛

为什么学习爬虫
    私人定制一个搜索引擎,并且可以对搜索引擎的数采集工作原理进行更深层次地理解
    获取更多的数据源,并且这些数据源可以按我们的目的进行采集,去掉很多无关数据
    更好的进行seo(搜索引擎优化)

网络爬虫的组成
    控制节点: 叫做爬虫中央控制器,主要负责根据URL地址分配线程,并调用爬虫节点进行具体爬行
    爬虫节点: 按照相关算法,对网页进行具体爬行,主要包括下载网页以及对网页的文本处理,爬行后会将对应的爬行结果存储到对应的资源库中
    资源库构成: 存储爬虫爬取到的响应数据,一般为数据库
    
爬虫设计思路
    首先确定需要爬取的网页URL地址
    通过HTTP协议来获取对应的HTML页面
    提取html页面里的有用数据
        如果是需要的数据就保存起来
        如果是其他的URL,那么就执行第二部
      

Python爬虫的优势

PHP: 虽然是世界上最好的语言,但是天生不是干爬虫的命,php对多线程,异步支持不足,并发不足,爬虫是工具性程序,对速度和效率要求较高。

Java: 生态圈完善,是PYthon最大的对手,但是java本身很笨重,代码量大,重构成本比较高,任何修改都会导致大量的代码的变动.最要命的是爬虫需要经常修改部分代码
# 爬虫 -> 反爬 -> 反反爬 -> 反反反爬 .... 

C/C++: 运行效率和性能几乎最强,但是学习成本非常高,代码成型较慢,能用C/C++写爬虫,说明能力很强,但不是最正确的选择.
    
# JS: DOM,事件,Ajax
Python: 语法优美,代码简洁,开发效率高,三方模块多,调用其他接口也方便, 有强大的爬虫Scrapy,以及成熟高效的scrapy-redis分布策略

Python爬虫需要掌握什么

Python基础语法
HTML基础

如何抓取页面: 
    HTTP请求处理,urllib处理后的请求可以模拟浏览器发送请求,获取服务器响应文件
解析服务器响应的内容: 
    re,xpath,BeautifulSoup4,jsonpath,pyquery
    目的是使用某种描述性语法来提取匹配规则的数据
如何采取动态html,验证码处理:
    通用的动态页面采集, Selenium+PhantomJs(无界面浏览器),模拟真实浏览器加载js,ajax等非静态页面数据
Scrapy框架
    国内常见的框架Scrapy,Pyspider
    高定制性高性能(异步网络框架twisted),所以数据下载速度非常快,提供了数据存储,数据下载,提取规则等组件
    (异步网络框架twisted类似tornado(和Django,Flask相比的优势是高并发,性能较强的服务器框架))
分布式策略 
    scrapy-redis
    在Scrapy的基础上添加了一套以redis数据库为核心的一套组件,让scrapy框架支持分布式的功能,主要在redis里做请求指纹去重,请求分配,数据临时存储
        MySql(持久化), Redis,Mongodb(缓存,速度快)
    

爬虫与反爬虫与反反爬虫三角之争

最头痛的人
    爬虫做到最后, 最头痛的不是复杂的页面, 也不是海量的数据, 而是网站另一头的反爬虫人员
反爬虫策略
    User-Agent
    代理IP
    验证码
    动态数据加载
    加密数据
是否需要反爬虫
    机器成本+人力成本 >  数据价值,就不反了,一般做到封IP就可以
    服务器压力
    面子的战争
    
爬虫和反爬虫之间的战争, 最后一定是爬虫胜利

网络爬虫类型

通用网络爬虫
    概念: 搜索引擎用的爬虫系统
    用户群体: 搜索引擎用的爬虫系统
    目标: 尽可能把互联网上的所有网页下载下来,放到本地服务器里形成备份,再对这些网页做相关处理(提取关键字,去掉广告等),最后提供一个用户检测接口 
    
    抓取流程: 
        首先选取一部分已有的URL,把这些URL放到待爬队列
        从队列里提取这些URL,然后解析DNS得到主机ip,然后去这个IP对应的服务器里下载HTML页面,保存到搜索引擎的本地服务器里,之后把爬过的URL放入到以爬取队列
        分析这些网页内容,找出网页里的URl连接,继续执行第二步,直到爬取条件结束
    
    搜索引擎如何获取一个新网站的URL:
        主动向搜索引擎提交网址(百度站长平台):http://zhanzhang.baidu.com/site/index?action=add
        在其他网站里设置网站外连接
        搜索引擎会和DNS服务商合作,可以快速收录新的网站
    
    通用爬虫并不是万物皆可爬的,它需要遵守规则:Robots协议
        协议会指明通用爬虫可以爬取网页的权限
        Robots.txt只是1个建议,并不是所有爬虫都遵守,一般只有大型的搜索引擎才会遵守
    
    通用爬虫工作流程:
        爬取网页
        存储数据
        内容处理
        提供检索/排名服务
    
    搜索引擎排名SEO:
        PageRank值:根据网站的流量(点击量,浏览值,人气)统计,流量越高,网站越值钱,排名越靠前
        竞价排名:谁钱多谁排名靠前
        
    通用爬虫的缺点
        只能提供和文本相关的内容(html,word,pdf)等,但是不能提供多媒体(音乐,图片,视频)和二进制文件(程序,脚本)等
        提供结果千篇一律,不能针对不同人群提供不同搜索结果
        不能理解人类语义上的检索
    
聚集网络爬虫
    概念:爬虫程序员写的针对某种内容的爬虫
    特点:面向主题爬虫,面向需求爬虫,会针对某种特定的内容去爬取信息,而且会保证信息和需求尽可能相关。
    
增量网络爬虫
深层网络爬虫

接下来,让我们真正迈向我们的爬虫开发之路吧!

二、Python2中开发爬虫

urllib2库的基本使用

所谓网页抓取,就是把URL地址中指定的网络资源从网络流中读取出来,保存到本地。 在Python中有很多库可以用来抓取网页,我们先学习urllib2。

urllib2 是 Python2.7 自带的模块(不需要下载,导入即可使用)
urllib2 官方文档:https://docs.python.org/2/library/urllib2.html
urllib2 源码:https://hg.python.org/cpython/file/2.7/Lib/urllib2.py

urllib2 在 python3.x 中被改为 urllib.request
示例: 使用urllib2抓取百度网页 ( 第一个爬虫程序 )
#!C:\Python36\python.exe
# -*- coding:utf-8 -*-

import urllib2

# url, 统一资源定位符
# data=None,默认为None为get请求,否则设置了data则是post请求
# timeout=超时时间
url = "http://www.baidu.com"
response = urllib2.urlopen(url)
print response  # socket._fileobject object, _fileobject文件对象

# python的文件操作
# print response.read()  # 返回所有内容,字符串
# print response.readline()  # 按行返回

# 读取所有行
# while True:
#     if not response.readline():
#         break
#     print response.readline()

print response.readlines()  # 返回所有行,列表

第一个反反爬

User-Agent
有一些网站不喜欢被爬虫程序访问,所以会检测连接对象,如果是爬虫程序,也就是非人点击访问,它就会不让你继续访问,所以为了要让程序可以正常运行,需要隐藏自己的爬虫程序的身份。此时,我们就可以通过设置User Agent的来达到隐藏身份的目的,User Agent的中文名为用户代理,简称UA。

User Agent存放于Headers中,服务器就是通过查看Headers中的User Agent来判断是谁在访问。在Python中,如果不设置User Agent,程序将使用默认的参数,那么这个User Agent就会有Python的字样,如果服务器检查User Agent,那么没有设置User Agent的Python程序将无法正常访问网站。

常用消息头(详解http请求消息头)
    Accept:text/html,image/*     (告诉服务器,浏览器可以接受文本,网页图片)
    Accept-Charaset:ISO-8859-1   [接受字符编码:iso-8859-1]
    Accept-Encoding:gzip,compress   [可以接受  gzip,compress压缩后数据]
    Accept-Language:zh-cn   [浏览器支持的语言]   
    Host:localhost:8080     [浏览器要找的主机]
    Referer:http://localhost:8080/test/abc.html     [告诉服务器我来自哪里,常用于防止下载,盗链]
    User-Agent:Mozilla/4.0(Com...)      [告诉服务器我的浏览器内核]
    Cookie: [会话]
    Connection:close/Keep-Alive     [保持链接,发完数据后,我不关闭链接]
    Date:   [浏览器发送数据的请求时间]

# 设置请求头的User-Agent
header = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"
}
# 构造一个请求对象发送请求,伪装浏览器访问
request = urllib2.Request(url, headers=header) 
添加更多的Header信息
在 HTTP Request 中加入特定的 Header,来构造一个完整的HTTP请求消息。

可以通过调用Request.add_header() 添加/修改一个特定的header 也可以通过调用Request.get_header()来查看已有的header。

添加一个特定的header
request.add_header("Connection", "keep-alive") # 一直活着

print request.get_header("User-agent")  # 用户代理, 首字母大写,其他小写
print request.get_full_url() # 访问的网页链接
print request.get_host() # 服务器域名
print request.get_method() # get或post
print request.get_type() # http/https/ftp

response = urllib2.urlopen(request)
print response.code # 状态码200, 404,500
print response.read() # 获取内容

data = response.read().decode("utf-8")  # 根据网页编码格式进行解码,常见编码:utf-8, gbk, gb2312...
print response.code # 响应状态码

url编码:urllib.urlencode()
我们都知道Http协议中参数的传输是"key=value"这种简直对形式的,如果要传多个参数就需要用“&”符号对键值对进行分割。如"?name1=value1&name2=value2",这样在服务端在收到这种字符串的时候,会用“&”分割出每一个参数,然后再用“=”来分割出参数值。

urllib 和urllib2 区别:
    urllib 和 urllib2 都是接受URL请求的相关模块,但是提供了不同的功能。两个最显著的不同如下:
    urllib 仅可以接受URL,不能创建 设置了headers 的Request 类实例;
    但是 urllib 提供 urlencode 方法用来GET查询字符串的产生,而 urllib2 则没有。(这是 urllib 和 urllib2 经常一起使用的主要原因)
    编码工作使用urllib的urlencode()函数,帮我们将key:value这样的键值对转换成"key=value"这样的字符串,解码工作可以使用urllib的unquote()函数。(注意,不是urllib2.urlencode())

urllib.urlencode(keyWord) # url编码
urllib.unquote(kw) # 解码
示例: 模拟百度搜索
#!C:\Python36\python.exe
# -*- coding:utf-8 -*-

import urllib2
import urllib

# 模拟百度搜索
def baiduAPI(params):
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"
    }

    url = "https://www.baidu.com/s?" + params
    request = urllib2.Request(url, headers=headers)
    response = urllib2.urlopen(request)
    return response.read()

if __name__ == "__main__":
    kw = raw_input("请输入你想查找的内容:")

    wd = {"wd": kw, 'ie': 'utf-8'}
    params = urllib.urlencode(wd)  # url编码:将字典 转换成 参数字符串
    # print wd  # 'wd=aa&ie=utf-8'

    content = baiduAPI(params)
    print content

三、Python3中开发爬虫

示例: urllib.request爬取百度
import urllib
from urllib import request

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"
}

# 创建请求对象
req = urllib.request.Request("http://www.baidu.com", headers=headers)

response = urllib.request.urlopen(req)
print(response.info())  # 响应信息
print(response.read())  # 二进制
print(response.read().decode('utf-8'))  # 字符串
示例:模拟百度搜索
import urllib.request
import urllib.parse

# 模拟百度搜索
def baiduAPI(params):

    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"
    }

    url = "https://www.baidu.com/s?" + params
    req = urllib.request.Request(url, headers=headers)
    response = urllib.request.urlopen(req)
    return response.read().decode('utf-8')

if __name__ == "__main__":
    kw = input("请输入你要查找的内容:")
    wd = {"wd": kw}
    params = urllib.parse.urlencode(wd)
    # print(params)  # 'wd=aa'

    response = baiduAPI(params)
    print(response)
示例: 爬取前程无忧岗位数量
import urllib
from urllib import request
import re

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"
}

# 前程无忧职位网址
url = "https://search.51job.com/list/000000,000000,0000,00,9,99,python,2,1.html?lang=c&stype=&postchannel=0000&workyear=99&cotype=99&degreefrom=99&jobterm=99&companysize=99&providesalary=99&lonlat=0%2C0&radius=-1&ord_field=0&confirmdate=9&fromType=&dibiaoid=0&address=&line=&specialarea=00&from=&welfare="

# 开始爬取
req = urllib.request.Request(url, headers=headers)
response = urllib.request.urlopen(req)

html = response.read().decode('gbk')  # HTML源码
# print(html)

jobnum_re = '<div class="rt">(.*?)</div>'

jobnum_comp = re.compile(jobnum_re, re.S)
jobnums = jobnum_comp.findall(html)
print(jobnums[0])

抓取ajax数据

示例:抓取豆瓣电影
import urllib
from urllib import request
import json

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"
}

url = "https://movie.douban.com/j/new_search_subjects?sort=T&range=0,10&tags=&start=0"
req = urllib.request.Request(url, headers=headers)
response = urllib.request.urlopen(req)
content = response.read().decode()  # json数据

data = json.loads(content)
movie_list = data.get('data')

for movie in movie_list:
    title = movie.get('title')
    casts = movie.get('casts')
    print(title, casts)
    

GET和POST请求

示例:POST爬取网易云音乐评论
import urllib.request
import urllib.parse
import json

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"
}

# post接口
url = "https://music.163.com/weapi/v1/resource/comments/R_SO_4_547976490?csrf_token="

# post提交参数
data = {
    "params": "3u5ErBfSCxBGdgjpJpTQyZVZgmPAv+aisCYZJ9pxk26DoOaS5on9xBjsE65yaS57u9XyxvCJIa78DXJathMsyiClN4LXqhonGNQrAtI2ajxsdW8FosN4kv8psGrRyCBsWrxSJQyfy5pfoeZwxLjB7jHtQkt9hglgZaAfj7ieRWq/XvX3DZtSgLcLrvH/SZOM",
    "encSecKey": "872312d7d8b04d2d5dab69d29c9bde5438337f0b3982887e3557468fe7b397de59e85ab349c07f32ef5902c40d57d023a454c3e1ed66205051264a723f20e61105752f16948e0369da48008acfd3617699f36192a75c3b26b0f9450b5663a69a7d003ffc4996e3551b74e22168b0c4edce08f9757dfbd83179148aed2a344826"}

# post参数为二进制
data = urllib.parse.urlencode(data).encode()
# print(data)

# 爬取(设置data进行POST请求)
req = urllib.request.Request(url, data=data, headers=headers)
response = urllib.request.urlopen(req)
content = response.read().decode()
# print(content)

hotcomments = json.loads(content)
hotcomments_list = hotcomments.get('hotComments')

# 热评
for c in hotcomments_list:
    userid = c['user']['userId']
    nickname = c['user']['nickname']
    content = c['content']
    print(userid, nickname, content)

练习: 抓取阿里招聘信息并保存到本地文件
# 抓取阿里招聘信息前5页数据,将每个岗位的信息一行行保存到ali.txt文件中;
# 岗位信息包括:学历degree,部门departmentName,岗位要求description,
#             类型firstCategory,要求workExperience

url = "https://job.alibaba.com/zhaopin/socialPositionList/doList.json"
for i in range(1, 6):
    params = {
        'pageSize': 10,
        't': '0.15338906034674604',
        'pageIndex': i
    }

下载

# 参数1: 需要下载的url
# 参数2: 需要写入的文件路径
request.urlretrieve("http://www.baidu.com", r"baidu.html")
request.urlcleanup()  # 清除缓存

# 下载图片
request.urlretrieve("https://www.baidu.com/img/bd_logo1.png", r"baidu.png")
request.urlcleanup()  # 清除缓存

处理HTTPS请求 SSL证书验证

现在随处可见 https 开头的网站,urllib2可以为 HTTPS 请求验证SSL证书,就像web浏览器一样,如果网站的SSL证书是经过CA认证的,则能够正常访问,如:https://www.baidu.com/等...

如果SSL证书验证不通过,或者操作系统不信任服务器的安全证书,比如浏览器在访问12306网站如:https://www.12306.cn/mormhweb/的时候,会警告用户证书不受信任。(据说 12306 网站证书是自己做的,没有通过CA认证)
示例:访问12306网站
import urllib
import urllib2
# 1. 导入Python SSL处理模块
import ssl

# 2. 表示忽略未经核实的SSL证书认证
context = ssl._create_unverified_context()

url = "https://www.12306.cn/mormhweb/"

headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}

request = urllib2.Request(url, headers = headers)

# 3. 在urlopen()方法里 指明添加 context 参数
response = urllib2.urlopen(request, context = context)

print response.read()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容