网络爬虫2-爬虫初步

网络爬虫2-爬虫初步

本章主要介绍初步的python爬虫方法
包括处理url参数,构建请求对象,模拟各种请求方式,handler处理器,opener和代理等

  1. url lib.parse()

    主要的方法用于处理参数或者url:

    • url.parse.quote()

      url中不能使用中文,使用的是url编码也可以说是百分号编码

      url中只能有字母、数字、下划线、冒号 // ? =等

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import urllib.parse

    url = 'https://www.baidu.com/s?ie=utf-8&f=3&rsv_bp=0&rsv_idx=1&tn=baidu&wd=%E5%B0%8F%E5%8F%AF%E7%88%B1&rsv_pq=96be106200031fd5&rsv_t=7e37zpPVn6Se%2FFpX446ehNfmIYvVrtgj1SOOkWgO48bW4gpLGLIloR6C3%2Fk&rqlang=cn&rsv_enter=1&rsv_sug3=11&rsv_sug1=3&rsv_sug7=101&rsv_sug2=0&prefixsug=%25E5%25B0%258F%25E5%258F%25AF%25E7%2588%25B1&rsp=3&inputT=3801&rsv_sug4=3801'

    string = urllib.parse.quote(url)
    string_reverse = urllib.parse.unquote(string)

    print(string)
    print(string_reverse)

    其中对url编码是对所有的(包括=号等都进行了编码),编码后不可访问,应该对中文参数单独进行编码;解码后可访问

    • urllib.parse.urlencode()

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      url = 'https://www.baidu.com/s?'
      data = {
      'ie': 'utf8',
      'wd': 'little'
      }
      # 将data拼接到url后面,组成完整的url
      # 方法:遍历这个字典,拼接为制定格式
      lt = []
      for k, v in data.items():
      value = k + '=' + v
      lt.append(value)
      # 将lt用&符号拼接
      query_string = '&'.join(lt)
      # 拼接url
      url += query_string

      print(url)

      以上的办法可以将data中的参数处理为url中可以携带的参数,但我们有更加简便的写法,直接使用urllib.parse.urlencode(data)方法

      1
      2
      3
      4
      # 使用urlencode()
      query_string = urllib.parse.urlencode(data)
      url += query_string
      print(url)

    也可以用url编码工具

  1. 构建请求对象

    不同的浏览器UA都是不同的,网站服务器根据UA给予不同的响应

    我们需要伪装UA进行请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import urllib.request
    import ssl

    ssl._create_default_https_context = ssl._create_unverified_context

    url = 'http://www.baidu.com/'

    # 如何定制UA
    # 在这个头部不仅可以定制ua,还可以定制其他的请求头部,一般只需要定制ua
    headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36'
    }

    # 构建请求对象
    request = urllib.request.Request(url=url, headers=headers)

    # 发送请求,直接打开这个请求对象即可
    response = urllib.request.urlopen(request)
  2. 模拟各种请求方式

    get

    模拟发送带参数的get
    例子:输入搜索国家,单独保存为txt文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    import urllib.request
    import urllib.parse

    # 让用户输入搜索关键字

    keyword = input('请输入要搜索的关键字:')
    url = 'https://www.baidu.com/s?'

    # get参数

    data = {
    'ie': 'utf8',
    'wd': keyword,
    }
    query_string = urllib.parse.urlencode(data)

    url += query_string

    # 向url发送请求,得到响应

    headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.3',
    }
    request = urllib.request.Request(url=url, headers=headers)

    response = urllib.request.urlopen(request)

    # 拼接文件名字

    filename = keyword + '.html'

    # 写入到文件中

    with open(filename, 'wb') as fp:
    fp.write(response.read())

    post

    例子:百度翻译
    调用百度翻译的sug接口,进行简单翻译

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import urllib.request
    import urllib.parse

    url = 'http://fanyi.baidu.com/sug'
    # 将表单数据写成一个字典
    formdata = {
    'kw': 'baby'
    }
    # 将formdata单独处理一下
    formdata = urllib.parse.urlencode(formdata).encode('utf8')
    headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.3',
    }
    # 构建请求对象
    request = urllib.request.Request(url=url, headers=headers)
    response = urllib.request.urlopen(request, data=formdata)

    print(response.read().decode('utf8'))
  • 调用百度翻译的接口,获取复杂一些的翻译

    在头部中除了CA还需要发送多个头部参数,可以得到单个针对性单词的翻译,但如果需要根据单词来查询需要破解百度翻译的加密sign和token

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    import urllib.request
    import urllib.parse

    # 加密的接口,如果想要得到,需要破解
    url = 'http://fanyi.baidu.com/v2transapi'

    word = 'wolf'
    # 表单数据
    formdata = {
    'from': 'en',
    'to': 'zh',
    'query': word,
    'transtype': 'realtime',
    'simple_means_flag': '3',
    'sign': '275695.55262',
    'token': '268ca3a468d99f5aac3a179efad0ab28',
    }
    # 处理表单数据
    formdata = urllib.parse.urlencode(formdata).encode('utf8')
    headers = {
    # 'Accept': '*/*',
    # 将其注释掉,索要完整的格式
    # 'Accept-Encoding': 'gzip, deflate',
    # 'Accept-Language': 'zh-CN,zh;q=0.9',
    # 'Connection': 'keep-alive',
    # 将其注释掉,让其自动计算即可
    # 'Content-Length': '120',
    # 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'Cookie': 'BAIDUID=55279ECD6DDA84C66A41BA7CC1E6840E:FG=1; PSTM=1533627007; BIDUPSID=6F6C332F8A0E3C9949BD5D9F884F1FFB; PSINO=3; BDRCVFR[Y1-7gJ950Fn]=jCHWiyEa0lYpAN8n1msQhPEUf; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; H_PS_PSSID=1465_26963_26432_21099_26350_26925_22157; locale=zh; to_lang_often=%5B%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%2C%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%5D; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1533694190; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1533694190; from_lang_often=%5B%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%2C%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%5D',
    'Host': 'fanyi.baidu.com',
    'Origin': 'http://fanyi.baidu.com',
    'Referer': 'http://fanyi.baidu.com/?aldtype=16047',
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36',
    'X-Requested-With': 'XMLHttpRequest',
    }
    request = urllib.request.Request(url=url, headers=headers)
    response = urllib.request.urlopen(request, data=formdata)

    print(response.read().decode('utf-8'))
    • 可能出现的错误有:

      {“error”:997,”from”:”en”,”to”:”zh”,”query”:”(‘wolf’,)”}:错误原因在于你没有模拟浏览器发送请求,请求头不一样,请求头有12个参数

    有一些请求头参数不是必须的,比如以下两个,去掉就可以了

​ ‘Accept-Encoding’: ‘gzip, deflate’:需要解压缩才能读取,所以直接去掉

​ ‘Content-Length’: ‘120’: 浏览器计算长度,去掉,让其自动计算

​ 加密的接口,需要破解

ajax-get

例子:豆瓣电影排行榜

获取豆瓣电影排行榜请求的ajax-get的url

让用户输入需要第几页的数据

通过分析我们可以得知,在豆瓣电影排行榜的url中的参数有start&limit两个,其中start是偏移量,limit是每一页显示的数量,每一页显示的睡昂是固定的10条,而偏移量start是等于(n-1)*10开始。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import urllib.request
import urllib.parse

url = 'https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&'
print('每页显示10条数据')
page = int(input('请输入页码:'))
# 根据page计算出来start和limit
start = (page-1) * 10
limit = 10
data = {
'start': start,
'limit': limit,
}
url += urllib.parse.urlencode(data)

headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36',
}
request = urllib.request.Request(url=url, headers=headers)

response = urllib.request.urlopen(request)


print(response.read().decode('utf8'))

ajax-post

在肯德基的官网上爬去城市门店的分布,其为post类型的ajax请求,我们下面模拟浏览器对官网发起请求,获取数据,而请求的url可以在下图中看到

例子:肯德基

肯德基

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import urllib.request
import urllib.parse

url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname'
city = input('请输入要搜索的城市:')
data = {
'cname': city,
'pid': '',
'pageIndex': '1',
'pageSize': '10'
}
data = urllib.parse.urlencode(data).encode('utf8')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36',
}
request = urllib.request.Request(url=url, headers=headers)

response = urllib.request.urlopen(request, data=data)

print(response.read().decode('utf8'))

  1. URLError\HTTPEroor

    异常处理类,属于urllib.error这个模块

  • URLError:断网或者主机不存在的时候会被触发

    为了使代码更加健壮,应该对可能出现异常的代码进行异常捕获,使用try,Exception进行捕获

    1
    2
    3
    4
    5
    6
    7
    url = 'http://www.maodan.com/'
    try:
    response = urllib.request.urlopen(url)
    except Exception as e:
    print(e)

    print('不影响后面的代码')
    • Exception: 是官方的异常基类,可以捕获所有的异常

      但是如果想精确捕获:需要指定具体的异常,如果指定错误当然就无法捕获了

  • HTTPEroor:文件不存在
    HTTPEroor是URLError的子类,如果多个except同时捕获,为了能够精确捕获应该把父类写在最后

  1. 复杂get

    例子:百度贴吧

    需求描述:输入贴吧名,输入需要爬取的气势和结束页码,以贴吧名字创建文件夹,将每一页的内容全部保存下来到第n页.html中
    我们需要有下载功能的函数,保存功能的函数两个函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    import urllib.request
    import urllib.parse
    import os
    import time

    def main():
    name = input('请输入要爬取的贴吧的名字:')
    start_page = int(input('请输入要爬取的起始页码:'))
    end_page = int(input('请输入要爬取的结束页码:'))
    url = 'https://tieba.baidu.com/f?'

    for page in range(start_page, end_page + 1):
    print('正在爬取第%s页......' % page)
    # 根据url和page拼接指定页码的url
    request = handle_request(page, baming, url)
    # 根据请求对象发送请求得到响应写入到指定的文件中
    down_load(request, baming, page)
    print('结束爬取第%s页' % page)
    time.sleep(3)

    # 模拟发起请求,保存数据到指定的地址
    def down_load(request, name, page):
    response = urllib.request.urlopen(request)
    # 通过代码创建指定的文件夹
    dirname = name
    # 判断不存在的时候创建
    if not os.path.exists(dirname):
    os.mkdir(dirname)
    # 文件的名字
    filename = '第%s页.html' % page
    # 得到文件的路径
    filepath = os.path.join(dirname, filename)
    # 将内容直接写入到filepath中
    with open(filepath, 'wb') as fp:
    fp.write(response.read())

    # 构建url和请求对象
    def handle_request(page, name, url):
    pn = (page-1) * 50
    # 拼接url
    data = {
    'kw': name,
    'ie': 'utf8',
    'pn': pn
    }
    url += urllib.parse.urlencode(data)
    # print(url)
    headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36',
    }
    # 构建请求对象
    request = urllib.request.Request(url, headers=headers)
    return request

    if __name__ == '__main__':
    main()
  2. Handler处理器,自定义Opener

    urlopen不能自己定制头部,引入自己定制的头部

    为了解决代理和cookie这些更加高级的功能,而引入handler代理

    首先我们来熟悉一下这个,使用它请求一个最简单的功能,高级功能的步骤是一摸一样的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import urllib.request

    url = 'http://www.baidu.com/'

    # 创建handler
    handler = urllib.request.HTTPHandler()
    # 根据handler创建opener
    opener = urllib.request.build_opener(handler)
    # 发送请求的时候不要使用urlopen使用opener.open发送
    response = opener.open(url)
    print(response.read().decode('utf8'))
  3. 代理

    访问不能过于频繁,需要休眠7-10秒左右

    可以设置多个代理,交替请求
    代理服务器:快代理,西祠代理,芝麻代理,阿布云代理等
    代理是需要购买,分为高匿代理,透明代理,普通代理
    代理有了ip和端口号就能使用
    验证代理是否可用,可以用程序,软件(花刺代理验证)等方式

  • 浏览器如何设置代理
    设置–>高级设置–>代理:前去配置代理的ip和端口
  • 代码中如何设置代理
    访问百度搜索的ip服务,可以看到ip地址已经变成了代理地址,已经在代码中使用了代理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    import urllib.request
    url = 'http://www.baidu.com/s?ie=UTF-8&wd=ip'
    handler = urllib.request.ProxyHandler(proxies={'http': '218.60.8.98:3129'})
    opener = urllib.request.build_opener(handler)

    r = opener.open(url)

    with open('代理.html', 'wb') as fp:
    fp.write(r.read())