天天看点

python学习笔记-爬虫01

#============================爬虫准备工作============================

# 参考资料

    # python网络数据采集, 图灵工业出版

    # 精通Python爬虫框架Scrapy, 人民邮电出版社

    # [Python3网络爬虫](http://blog.csdn.net/c406495762/article/details/72858983)

    # [Scrapy官方教程](http://scrapy-chs.readthedocs.io/zh_CN/0.24/intro/tutorial.html)

# 前提知识

    # url

    # http协议

    # web前端,html, css, js

    # ajax

    # re, xpath

    # xml

#============================爬虫简介============================

# 爬虫定义:网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),

        #是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。

        #另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。

# 两大特征

    # 能按作者要求下载数据或者内容

    # 能自动在网络上流窜

# 三大步骤:

    # 下载网页

    # 提取正确的信息

    # 根据一定规则自动跳到另外的网页上执行上两步内容

# 爬虫分类

    # 通用爬虫

    # 专用爬虫(聚焦爬虫)

# Python网络包简介

    # Python2.x:urllib, urllib2, urllib3, httplib, httplib2, requests

    # Python3.x: urllib, urllib3, httplib2, requests

    # python2: urllib和urllib2配合使用,或者requests

    # Python3: urllib,requests

#==========================urllib=========================

# 包含模块

    # urllib.request: 打开和读取urls

    # urllib.error: 包含urllib.request产生的常见的错误,使用try捕捉

    # urllib.parse:  包含解析url的方法

    # urllib.robotparse: 解析robots.txt文件

    # 案例v1:

    from urllib import request
    url = "http://jobs.zhaopin.com/..."
    # 打开相应url并把相应页面作为返回
    rsp = request.urlopen(url)
    # 把返回结果读取出来
    # 读取出来内容类型为bytes
    html = rsp.read()
    # 如果想把bytes内容转换成字符串,需要解码
    html = html.decode("utf-8")
           

# 网页编码问题解决

    # chardet 可以自动检测页面文件的编码格式,但是,可能有误

    # 需要安装, conda install chardet

    # 案例v2片段:

    #利用 chardet自动检测
    cs = chardet.detect(html)
    # 使用get取值保证不会出错
    html = html.decode(cs.get("encoding", "utf-8"))
           

# urlopen 的返回对象

    # geturl: 返回请求对象的url

    # info: 请求反馈对象的meta信息

    # getcode:返回的http code    

# request.data 的使用

    # 访问网络的两种方法

        # get: 

            # 利用参数给服务器传递信息,

            # 参数为dict,然后用parse编码

            # 案例v4片段:

from urllib import request, parse

url = 'http://www.baidu.com/s?'
wd = input("Input your keyword:")

# 要想使用data, 需要使用字典结构
qs = {"wd": wd}
# 转换url编码
qs = parse.urlencode(qs)
fullurl = url + qs
# 如果直接用可读的带参数的url,是不能访问的
#fullurl = 'http://www.baidu.com/s?wd=大熊猫'
rsp = request.urlopen(fullurl)
html = rsp.read()
           

        # post

            # 一般向服务器传递参数使用

            # post是把信息自动加密处理

            # 我们如果想使用post信息,需要用到data参数

            # 使用post,意味着Http的请求头可能需要更改:

                # Content-Type: application/x-www.form-urlencode

                # Content-ength: 数据长度

                # 简而言之,一旦更改请求方法,请注意其他请求头部信息相适应

            # urllib.parse.urlencode可以将字符串自动转换成上面的

            # 案例v5:经典案例:

'''
利用parse模块模拟post请求
分析百度词典
分析步骤:
1. 打开F12
2. 尝试输入单词girl,发现每敲一个字母后都有请求
3. 请求地址是 http://fanyi.baidu.com/sug
4. 利用NetWork-All-Hearders,查看,发现FormData的值是 kw:girl
5. 检查返回内容格式,发现返回的是json格式内容==>需要用到json包
'''
from urllib import request, parse
# 负责处理json格式的模块
import json
'''
大致流程是:
1. 利用data构造内容,然后urlopen打开
2. 返回一个json格式的结果
3. 结果就应该是girl的释义
'''
baseurl = 'http://fanyi.baidu.com/sug'
# 存放用来模拟form的数据一定是dict格式
data = {
    # girl是翻译输入的英文内容,应该是由用户输入,此处使用硬编码
    'kw': 'girl'
}
# 需要使用parse模块对data进行编码
data = parse.urlencode(data).encode("utf-8")
rsp = request.urlopen(baseurl, data=data)
json_data = rsp.read().decode('utf-8')
# 把json字符串转化成字典
json_data = json.loads(json_data)
print(type(json_data))
print(json_data)
for item in json_data['data']:
    print(item['k'], "--", item['v'])
           

            # 为了更多的设置请求信息,单纯的通过urlopen函数已经不太好用了

            # 需要利用request.Request 类

                        # 构造一个Request的实例

                        # 我们需要构造一个请求头,请求头部应该至少包含传入的数据的长度

                        # request要求传入的请求头是一个dict格式

                        headers = {

                            # 因为使用post,至少应该包含content-length 字段

                            'Content-Length':len(data)

                        }

                        # 有了headers,data,url,就可以尝试发出请求了

                        req = request.Request(url=baseurl, data=data, headers=headers)

                        # 因为已经构造了一个Request的请求实例,则所有的请求信息都可以封装在Request实例中

                        rsp = request.urlopen(req)

# urllib.error

    # URLError产生的原因:

        # 没网

        # 服务器链接失败

        # 知不道制定服务器

        # 是OSError的子类

    # HTTPError, 是URLError的一个子类

        # 案例v8      

from urllib import request, error
try:
    req = request.Request(url)
    rsp = request.urlopen(req)
    html = rsp.read().decode()
    print(html)
except error.HTTPError as e:
    print("HTTPError: {0}".format(e.reason))
    print("HTTPError: {0}".format(e))
except error.URLError as e:
    print("URLError: {0}".format(e.reason))
    print("URLError: {0}".format(e))
except Exception as e:
    print(e)
           

    # 两者区别:

        # HTTPError是对应的HTTP请求的返回码错误, 如果返回错误码是400以上的,则引发HTTPError

        # URLError对应的一般是网络出现问题,包括url问题

        # 关系区别: OSError-URLError-HTTPError

# UserAgent 

    # UserAgent: 用户代理,简称UA, 属于heads的一部分,服务器通过UA来判断访问者身份

    # 常见的UA值,使用的时候可以直接复制粘贴,也可以用浏览器访问的时候抓包

            1.Android

            Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19

            Mozilla/5.0 (Linux; U; Android 4.0.4; en#gb; GT#I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30

            Mozilla/5.0 (Linux; U; Android 2.2; en#gb; GT#P1000 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1

            2.Firefox

            Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0

            Mozilla/5.0 (Android; Mobile; rv:14.0) Gecko/14.0 Firefox/14.0

            3.Google Chrome

            Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36

            Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19

            4.iOS

            Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3

            Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/3A101a Safari/419.3

    # 设置UA可以通过两种方式:

        # heads

        # add_header

        # 使用head方法伪装UA

        # headers = {}

        # headers['User-Agent'] = 'Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3'

        # req = request.Request( url, headers=headers)

        # 使用add_header方法

        req = request.Request(url)

        req.add_header("User-Agent", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36")

        # 正常访问

        rsp = request.urlopen( req )

        html = rsp.read().decode()

        print(html)

# ProxyHandler处理(代理服务器)

    # 使用代理IP,是爬虫的常用手段

    # 获取代理服务器的地址:

        # www.xicidaili.com

        # www.goubanjia.com

    # 代理用来隐藏真实访问中,代理也不允许频繁访问某一个固定网站,所以,代理一定要很多很多

    # 基本使用步骤:

    from urllib import  request, error
    url = "http://www.baidu.com"
    # 使用代理步骤
    # 1. 设置代理地址
    proxy = {'http': '120.194.18.90:81' }
    # 2. 创建ProxyHandler
    proxy_handler = request.ProxyHandler(proxy)
    # 3. 创建Opener
    opener = request.build_opener(proxy_handler)
    # 4. 安装Opener
    request.install_opener(opener)
    # 现在如果访问url,则使用代理服务器
    try:
        rsp = request.urlopen(url)
        html = rsp.read().decode()
        print(html)
    except error.URLError as e:
        print(e)
    except Exception as e:
        print(e)
           

# cookie & session

    # 由于http协议的无记忆性,人们为了弥补这个缺憾,所采用的一个补充协议

    # cookie是发放给用户(即http浏览器)的一段信息,session是保存在服务器上的对应的另一半信息,用来记录用户信息

# cookie和session的区别

    # 存放位置不同

    # cookie不安全

    # session会保存在服务器上一定时间,会过期

    # 单个cookie保存数据不超过4k, 很多浏览器限制一个站点最多保存20个

# session的存放位置

    # 存在服务器端

    # 一般情况,session是放在内存中或者数据库中

    # 没有cookie登录,反馈网页为未登录状态

# 使用cookie登录

    # 直接把cookie复制下来,然后手动放入请求头:

    headers = {

        "Cookie": "xxxx"

    }

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

    # http模块包含一些关于cookie的模块,通过他们我们可以自动使用cookie

        # CookieJar

            # 管理存储cookie,向传出的http请求添加cookie,

            # cookie存储在内存中,CookieJar实例回收后cookie将消失

        # FileCookieJar(filename, delayload=None, policy=None):

            # 使用文件管理cookie

            # filename是保存cookie的文件

        # MozillaCookieJar(filename, delayload=None, policy=None):

            # 创建与mozilla浏览器cookie.txt兼容的FileCookieJar实例

        # LwpCookieJar(filename, delayload=None, policy=None):

            # 创建与libwww-perl标准兼容的Set-Cookie3格式的FileCookieJar实例

        # 他们的关系是: CookieJar-->FileCookieJar-->MozillaCookieJar & LwpCookieJar

    # handler是Handler的实例

        # 用来处理复杂请求 

    # 创立handler后,使用opener打开,打开后相应的业务由相应的hanlder处理

    # cookie的保存-FileCookieJar

        cookie.save(ignore_discard=True, ignore_expires=True)

    # cookie的读取:

        cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)

    # 案例:利用cookiejar访问网站

# 自动使用cookie登录,大致流程是
# 打开登录页面后自动通过用户名密码登录
# 自动提取反馈回来的cookie
# 利用提取的cookie登录隐私页面    

from urllib import request, parse
from http import cookiejar

# 创建cookiejar的实例
# cookie = cookiejar.CookieJar()
# 需要保存成文本时创建MozillaCookieJar或FileCookieJar的实例
filename = "cookie.txt"
cookie = cookiejar.MozillaCookieJar(filename)
# cookie的读取:
# cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)
# 生成 cookie的管理器
cookie_handler = request.HTTPCookieProcessor(cookie)
# 创建http请求管理器
http_handler = request.HTTPHandler()
# 生成https管理器
https_handler = request.HTTPSHandler()
# 创建请求管理器
opener = request.build_opener(http_handler, https_handler, cookie_handler)
def login():
    '''
    负责初次登录
    需要输入用户名密码,用来获取登录cookie凭证
    :return:
    '''
    # 此url需要从登录form的action属性中提取
    url = "http://www.renren.com/PLogin.do"
    # 此键值需要从登录form的两个对应input中提取name属性
    data = {
        "email": "13119144223",
        "password": "123456"
    }
    # 把数据进行编码
    data = parse.urlencode(data)
    # 创建一个请求对象
    req = request.Request(url, data=data.encode())
    # 使用opener发起请求
    rsp = opener.open(req)
    
    # 保存cookie到文件
    # ignor_discard表示即使cookie将要被丢弃也要保存下来
    # ignore_expire表示如果该文件中cookie即使已经过期,保存
    cookie.save(ignore_discard=True, ignore_expires=True)
    
def getHomePage():
    url = "http://www.renren.com/965187997/profile"
    # 如果已经执行了login函数,则opener自动已经包含相应的cookie值
    rsp = opener.open(url)
    html = rsp.read().decode()
   
if __name__ == '__main__':
    login()
    getHomePage()
           

    # cookie作为一个变量,打印出来, 案例片段:

    print(cookie)
    for item in cookie:
        print(type(item))
        print(item)
        for i in dir(item):
            print(i)
           

    # cookie的属性

        # name: 名称

        # value: 值

        # domain:可以访问此cookie的域名

        # path: 可以访问此cookie的页面路径

        # expires:过期时间

        # size: 大小

        # Http字段

# SSL

    # SSL证书就是指遵守SSL安全套阶层协议的服务器数字证书(SercureSocketLayer)

    # 美国网景公司开发

    # CA(CertifacateAuthority)是数字证书认证中心,是发放,管理,废除数字证书的收信人的第三方机构

    # 遇到不信任的SSL证书,需要单独处理,案例:

# 导入pythopn ssl处理模块
import ssl

# 利用非认证上下文环境替换认证的向下文环境
ssl._create_default_https_context = ssl._create_unverified_context
url = "https://www.12306.cn/mormhweb/"
rsp = request.urlopen(url)
html = rsp.read().decode()
           

# js加密

    # 有的反爬虫策略采用js对需要传输的数据进行加密处理(通常是取md5值)

    # 经过加密,传输的就是密文,但是

    # 加密函数或者过程一定是在浏览器完成,也就是一定会把代码(js代码)暴露给使用者

    # 通过阅读加密算法,就可以模拟出加密过程,从而达到破解

    # 过程参看案例v19

    # 注意salt,sign的计算

# ajax

    # 异步请求

    # 一定会有url,请求方法,可能有数据

    # 一般使用json格式

    # 案例,爬去豆瓣电影, 案例v20

    # 注意请求值

#================================Requests==========================

# HTTP for Humans,更简洁更友好

# 继承了urllib的所有特征

# 底层使用的是urllib3

# 开源地址: https://github.com/requests/requests

# 中文文档: http://docs.python#requests.org/zh_CN/latest/index.html   

# 安装: conda install requests

# get请求

    # requests.get(url)

    # requests.request("get", url)

    # 可以带有headers和parmas参数

import requests

url = "http://www.baidu.com"
# 两种请求方式
# 使用get请求
rsp = requests.get(url)
print(rsp.text)

# 使用request请求
rsp = requests.request("get", url, params=kw, headers=headers)
print(rsp.text)

# get返回内容
print(rsp.text)
print(rsp.content)
print(rsp.url)
print(rsp.encoding)
print(rsp.status_code) # 请求返回码
           

    # post

rsp = requests.post(baseurl, data=data,  headers=headers)
           

    # date, headers要求dict类型,不需要编码成byte流

   # proxy

   # 

proxies = {
"http":"address of proxy",
"https": "address of proxy"
}
rsp = requests.request("get", "http:xxxxxx", proxies=proxies)
           

# 代理有可能报错,如果使用人数多,考虑安全问题,可能会被强行关闭

# 用户验证

    # 代理验证

#可能需要使用HTTP basic Auth, 可以这样
# 格式为  用户名:密码@代理地址:端口地址
proxy = { "http": "china:[email protected]:4444"}
rsp = requests.get("http://baidu.com", proxies=proxy)
           

# web客户端验证

    # 如果遇到web客户端验证,需要添加auth=(用户名,密码)

autu=("test1", "123456")#授权信息
rsp = requests.get("http://www.baidu.com", auth=auth)
           

# cookie

# requests可以自动处理cookie信息
rsp = requests.get("http://xxxxxxxxxxx")
# 如果对方服务器给传送过来cookie信息,则可以通过反馈的cookie属性得到
# 返回一个cookiejar实例
cookiejar = rsp.cookies   
#可以讲cookiejar转换成字典
cookiedict = requests.utils.dict_from_cookiejar(cookiejar)         
           

# session

    # 跟服务器端session不是一个东东

    # 模拟一次会话,从客户端浏览器链接服务器开始,到客户端浏览器断开

    # 能让我们跨请求时保持某些参数,比如在同一个session实例发出的 所有请求之间保持cookie

# 创建session对象,可以保持cookie值
ss = requests.session()
headers = {"User-Agent":"xxxxxxxxxxxxxxxxxx"}
data = {"name":"xxxxxxxxxxx"}
# 此时,由创建的session管理请求,负责发出请求,
ss.post("http://www.baidu.com", data=data, headers=headers)
rsp = ss.get("xxxxxxxxxxxx")
           

# https请求验证ssl证书

    # 参数verify负责表示是否需要验证ssL证书,默认是True

    # 如果不需要验证ssl证书,则设置成False表示关闭

    # 如果用verify=True访问12306,会报错,因为他证书有问题 

rsp = requests.get("https://www.baidu.com", verify=False)