爬虫

作者: riverstation | 来源:发表于2018-07-09 20:08 被阅读158次

    上网原理

    上网原理.png

    1、爬虫概念

    爬虫是什麽?

    蜘蛛,蛆,代码中,就是写了一段代码,代码的功能从互联网中提取数据

    互联网:

    节点就是url(统一资源定位符),有很多a链接组成

    互联网爬虫:

    写代码,模拟浏览器访问url,并且从里面提取指定的内容

    都有哪些语言可以实现爬虫?

    1.php。号称世界上最优美的语言,php天生对多进程多线程支持的不好
    2.java。是python最主要的竞争对手,也说它不好,java语言比较臃肿,重构起来难度太大
    3.c,c++。也能够实现,不是最好的选择,只能说是能力的体现
    4.python,号称世界上最美丽的语言,代码简洁,学习成本低,支持的模块多,scrapy爬虫框架

    通用爬虫

    百度、360、搜狗、谷歌、必应等,搜索引擎

    (1)从互联网上抓取所有的数据
    (2)对数据进行处理
    (3)对用户提供检索服务
    百度如何爬取新的内容?
    (1)主动的将域名提交给百度
    (2)在其他网站中设置友情链接
    (3)自己会和dns服务商进行合作

    robots协议:

    就是一个君子协议,淘宝就不允许百度抓取

    这个协议就是一个文件,要放到网站的根目录

    咱写的爬虫程序,你就不用遵从了

    排名:

    (1)根据一个值排名,pagerank,(seo)

    (2)竞价排名,为浙西事件

    缺点:

    (1)抓取的很多数据都是没用的

    (2)不能根据需求进行爬取


    聚焦爬虫

    根据自己的需求,提取指定的数据
    我们学习的就是聚焦爬虫

    • 如何实现抓取指定的数据:
      (1)每一个网页都有自己的唯一的url
      (2)网页都是由html组成的
      (3)网页的传输使用的都是http、https协议

    • 爬虫的思路:

    (1)要爬取的网页的url
    (2)写代码模拟浏览器发送http请求
    (3)解析网页内容,字符串处理,根据规则提取数据

    开发环境

    windows、linux都可以

    python 3.x 64位
    编辑工具:pycharm sublime
    整体内容介绍:
    (1)模拟浏览器发送请求
    urllib.request urllib.parse requests
    (2)解析内容
    正则表达式、bs4、xpath、jsonpath
    (3)动态html
    selenium+phantomjs
    (4)scrapy框架的学习
    (5)scrapy-redis的组件
    (6)涉及到的爬虫-反爬虫-反反爬虫的内容

    User-Agent、代理、验证码、动态数据
    最终:理论上,只要浏览器能够访问的数据,你的程序就能访问
    模拟登录

    2、http协议

    上网原理:看图形

    http和https的区别:

    2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。

    3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

    4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

    http协议 https://www.cnblogs.com/10158wsj/p/6762848.html

    一个完整的url

        http://www.baidu.com:80/index.html?username=goudan&password=123456&lala=xixi#dudu
    

    协议 域名 端口号 请求的资源 get参数 锚点
    锚点:可以实现同一个页面的跳转

    请求:
    请求行、请求头、空行
    常见请求头

    accept:浏览器通过这个头告诉服务器,它所支持的数据类型

    Accept-Charset: 浏览器通过这个头告诉服务器,它支持哪种字符集

    Accept-Encoding:浏览器通过这个头告诉服务器,支持的压缩格式(重要)

    Accept-Language:浏览器通过这个头告诉服务器,它的语言环境

    Host:浏览器通过这个头告诉服务器,想访问哪台主机

    If-Modified-Since: 浏览器通过这个头告诉服务器,缓存数据的时间

    Referer:浏览器通过这个头告诉服务器,客户机是哪个页面来的 防盗链(重要)

    Connection:浏览器通过这个头告诉服务器,请求完后是断开链接还是何持链接

    User-Agent:客户端的类型

    cookie:和身份有关的

    响应:状态行、响应头、实体内容

    常见响应头
    Location: 服务器通过这个头,来告诉浏览器跳到哪里
      
    Server:服务器通过这个头,告诉浏览器服务器的型号
      
    Content-Encoding:服务器通过这个头,告诉浏览器,数据的压缩格式
      
    Content-Length: 服务器通过这个头,告诉浏览器回送数据的长度
      
    Content-Language: 服务器通过这个头,告诉浏览器语言环境
      
    Content-Type:服务器通过这个头,告诉浏览器回送数据的类型
      
    Refresh:服务器通过这个头,告诉浏览器定时刷新
      
    Content-Disposition: 服务器通过这个头,告诉浏览器以下载方式打数据
      
    Transfer-Encoding:服务器通过这个头,告诉浏览器数据是以分块方式回送的
      
    **Expires: -1 **控制浏览器不要缓存
      Cache-Control: no-cache
      Pragma: no-cache

    3、抓包工具

    sublime安装和使用

    • (1)安装插件管理器,packagecontrol
    • (2)安装插件
      按:ctrl + shift + p
      输入pci,点击installpackage即可
      搜索想要安装的插件,敲enter即可安装
    • (3)卸载插件
      按:ctrl + shift + p
      输入remove,选中remove package即可

    抓包:

    谷歌浏览器自带抓包工具
    network,点击看详情
    response.headers 响应头部
    request.headers 请求头
    query_string 请求字符串,get参数
    formadata 请求参数,post参数
    response:响应内容
    fiddler软件
    专业的抓包软件

    配置:

    4、urllib库

    是干什么的?是python用来模拟http发送请求的,是python自带的一个模块
    python2.x :urllib,urllib2
    python3.x :urllib
    urllib.request : 发送请求、获取响应
    urlopen(url=xxx, data=xxx) : 发送get,直接将参数拼接到url的后面,发送post,就需要data参数了
    response对象:

    read() :

    读取的为二进制数据
    字符串类型《====》二进制类型

    encode() :

    字符串格式转化为二进制格式,不写代表utf8,写gbk就代表gbk

    decode() :

    二进制转为字符串,不写就是utf8,写gbk就是gbk

    readlines() :

    读取为列表格式,按行读取

    getcode() :

    获取状态码

    geturl() :

    获取请求的url

    getheaders() :

    返回响应头部,列表里面有元组

    urlretrieve() :

    发送请求,并且将响应信息直接写入到文件中

    urllib.parse :

    用来处理数据

    quote() :

    对url里面的非法字符进行编码,字母、数字、下划线、&、://

    https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=78000241_9_hao_pg&wd=%E5%B8%85%E5%93%A5&oq=%25E5%25A7%259A%25E5%25B9%25BF%25E9%2591%25AB&rsv_pq=e7c2fda20000674b&rsv_t=9e64HlBZF4gMz%2Bxagmv2AtmJhzUYbJULwJToXPa78kQ%2BBslZuQB%2BshDyF%2BWpOTya0wv%2BZEwsI98&rqlang=cn&rsv_enter=1&inputT=44615&rsv_sug3=45&rsv_sug1=33&rsv_sug7=101&bs=%E5%A7%9A%E5%B9%BF%E9%91%AB

    unquote() :

    url解码

    urlencode() :

    传递一个字典,将字典转化为

    query_stirng ,

    并且里面的非法字符得到编码

    5、构建请求对象

    User-Agent : 客户端浏览器类型

    定制请求头部,将自己伪装成pc浏览器

    headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT                   6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
                }
    
        # 构建请求对象
    request = urllib.request.Request(url=url, headers=headers)
    




    fiddler使用文档

    1、配置

    2、使用

    左边:所有的请求

    <> : 返回的是html文档
    图片标记:代表是图片
    {json} : 返回json格式
    {css} : css文件
    js: js文件

    右边:查看某个请求的详细信息

    inspectors

    右上:这个请求所有的请求信息

    raw:请求的原始信息
    webforms:请求所带参数,
    query_string代表get参数,formdata代
    表post参数

    右下:所有的响应信息

    raw:查看详细的响应信息
    json:如果返回的是json数据,在这里
    查看

    清除所有请求

    removeall 清除所有请求
    file==》capture 关闭或者启动抓包

    快捷键:

    select html : 所有的html请求
    select js : 所有的js请求
    ?baidu : 选中所有带有baidu的请求
    cls : 清除所有的请求

    地址编码

    import urllib.parse
    # url = 'http://www.baidu.com/index.html?name=狗蛋'
    # str1 = urllib.parse.quote(url)
    url = 'http://www.baidu.com/index.html?'
    data = {
        'username':'狗蛋',
        'pwd':'jsjsjs',
        'height':'175',
    
    
    }
    
    string1 = urllib.parse.urlencode(data)
    print(string1)
    url+=string1
    print(url)
    
    
    
    

    图片保存

    
    #
    import urllib.request
    url = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1531147620161&di=cc9bfc90e915669e609905a13b01affd&imgtype=0&src=http%3A%2F%2Fnres.ingdan.com%2Fuploads%2F20151210%2F1449737241395228.jpg'
    response = urllib.request.urlopen(url)
    urllib.request.urlretrieve(url,'tu.jpg')
    

    网页文件down

    
    import urllib.request
    url = 'http://www.sougou.com/'
    response = urllib.request.urlopen(url)
    with open('sougouq.html','w',encoding='utf-8') as fp:
        fp.write(response.read().decode())
    print(response.getcode())
    
    
    
    

    post请求

    #百度翻译初级入门
    import urllib.request
    import urllib.parse
    
    post_url = 'http://fanyi.baidu.com/v2transapi'
    
    #data
    # word = input('请输入')
    word = 'river'
    formdata = {
        'from': 'en',
        'to':'zh',
        'query': word,
        'transtype': 'realtime',
        'simple_means_flag': '3',
        'sign': '555962.825483',
        'token': '8d393681119b809e14f9636ea55337c5',
    }
    
    formdata = urllib.parse.urlencode(formdata).encode('utf8')
    
    headers = {
        'Accept': '*/*',
    
    'Accept-Language': 'zh-CN,zh;q=0.9',
    'Connection': 'keep-alive',
    'Content-Length': '123',
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'Cookie': 'BAIDUID=9AB6C0FBB652B8E5C0EB2116B4794D8A:FG=1; BIDUPSID=9AB6C0FBB652B8E5C0EB2116B4794D8A; PSTM=1519631202; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; BDUSS=DJsdjlXaDlrZm5TUjdkY0NjSU05SWdaM2dxYlh4dFpwcXlQWnJtVFFSa2JKUnBiQUFBQUFBJCQAAAAAAAAAAAEAAAC8V7VPdGltZbXEcml2ZXIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABuY8lobmPJaW; H_PS_PSSID=26522_1458_21108_18560_22073; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; 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; 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; PSINO=2; locale=zh; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1531202878,1531203779,1531203784; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1531203784',
    '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; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    'X-Requested-With': 'XMLHttpRequest'
    }
    #构建请求对象
    
    # request = urllib.request.Request(url=post_url,headers=headers)
    # #获取相应
    # resposne = urllib.request.urlopen(request,data=formdata)
    # print(resposne.read().decode('utf8'))
    request = urllib.request.Request(url=post_url,headers=headers)
    response = urllib.request.urlopen(request,data=formdata)
    print(response.read().decode('utf8'))
    

    get请求

    import urllib.request
    import urllib.parse
    
    url = 'https://www.baidu.com/s?'
    
    # word = input('请输入')
    word = '烟台'
    data = {
        'ie':'utf8',
        'wd':word
    }
    quit_string = urllib.parse.urlencode(data)
    url+=quit_string
    
    
    headers = {
         'User-Agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Mobile Safari/537.36'
    
    }
    
    request = urllib.request.Request(url=url,headers=headers)
    response = urllib.request.urlopen(request)
    with open( '{}.html'.format(word),'w',encoding='utf-8') as fp:
        fp.write(response.read().decode('utf8'))
    

    ajax get请求

    # ajax-get
    
    import urllib.request
    import urllib.parse
    
    url = 'https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&'
    
    
    print('每页显示二十条数据')
    # page = input('请输入你要几页的数据')
    page = 4
    start = (page-1)*20
    limit = 20
    
    data = {
        'start':start,
        'limit':limit,
    
    }
    
    
    qury_string = urllib.parse.urlencode(data)
    url +=qury_string
    
    headers = {
         'User-Agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Mobile Safari/537.36'
    
    }
    
    request = urllib.request.Request(url = url,headers=headers)
    
    response = urllib.request.urlopen(request)
    print(response.read().decode('utf8'))
    
    

    ajax post 请求

    # ajax-post
    # kfc
    import urllib.request
    import urllib.parse
    post_url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname'
    
    
    data = {
       'cname':'北京',
       'pageIndex':'1',
       'pid':'',
       'pageSize':'10',
           
    }
    
    
    data = urllib.parse.urlencode(data).encode('utf8')
    
    headers = {
        'User-Agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Mobile Safari/537.36'
    
    }
    request = urllib.request.Request(url=post_url,headers=headers)
    
    resposne = urllib.request.urlopen(request,data=data)
    print(resposne.read().decode('utf8'))
    
    
    

    爬虫2

    1模拟各种请求方式

    get请求

    import urllib.request
    import urllib.parse
    
    url = 'https://www.baidu.com/s?'
    word = input('请输入要查询的内容:')
    # get参数写到这里
    data = {
        'ie': 'utf8',
        'wd': word,
    }
    
    # 拼接url
    query_string = urllib.parse.urlencode(data)
    
    url += query_string
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    
    # 构建请求对象
    request = urllib.request.Request(url=url, headers=headers)
    
    response = urllib.request.urlopen(request)
    
    filename = word + '.html'
    # 写入到文件中
    with open(filename, 'wb') as fp:
        fp.write(response.read())
    
    # 插件,sublime-repl
    


    post请求

    • 百度翻译 XMLHttpRequest
    import urllib.request
    import urllib.parse
    
    post_url = 'http://fanyi.baidu.com/sug'
    # word = input('请输入要查询的英文单词:')
    word = 'baby'
    # post携带的参数
    data = {
        'kw': word
    }
    # 对post参数进行处理
    data = urllib.parse.urlencode(data).encode('utf8')
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    # 构建请求对象
    request = urllib.request.Request(url=post_url, headers=headers)
    # 发送请求
    response = urllib.request.urlopen(request, data=data)
    
    print(response.read().decode('utf8'))
    
    

    进阶

    import urllib.request
    import urllib.parse
    
    post_url = 'http://fanyi.baidu.com/v2transapi'
    
    # post参数
    formdata = {
        'from': 'en',
        'to': 'zh',
        'query': 'baby',
        'transtype': 'realtime',
        'simple_means_flag': '3',
        'sign': '814534.560887',
        'token': '921cc5d0819e0f1b4212c7fdc3b23866',
    }
    
    # 处理表单数据
    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': '121',
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Cookie': 'BAIDUID=59CCBF5477FDC81799054A3DA85BEF88:FG=1; BIDUPSID=59CCBF5477FDC81799054A3DA85BEF88; PSTM=1529815427; pgv_pvi=2975680512; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; 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; 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; H_PS_PSSID=1436_26432_21094_20929; PSINO=2; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1531189205,1531202737; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1531202737',
        '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; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
        'X-Requested-With': 'XMLHttpRequest',
    }
    
    # 构建请求对象,发送请求
    request = urllib.request.Request(url=post_url, headers=headers)
    
    # 发送请求
    response = urllib.request.urlopen(request, data=formdata)
    
    print(response.read().decode('utf-8'))
    



    ajax-get请求

    import urllib.request
    import urllib.parse
    
    url = 'https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&'
    
    print('每页显示20条数据')
    # page = input('请输入要第几页数据:')
    page = 3
    
    # 根据page就可以计算出来start和limit的值
    start = (page-1) * 20
    limit = 20
    
    # 写get参数
    data = {
        'start': start,
        'limit': limit,
    }
    
    # 将字典转化为query_string格式
    query_string = urllib.parse.urlencode(data)
    
    # 拼接url
    url += query_string
    
    # print(url)
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    request = urllib.request.Request(url=url, headers=headers)
    
    response = urllib.request.urlopen(request)
    
    print(response.read().decode('utf8'))
    

    ajax-post请求

    • 肯德基店铺位置
    import urllib.request
    import urllib.parse
    
    post_url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname'
    data = {
        'cname': '石河子',
        'pid': '',
        'pageIndex': '1',
        'pageSize': '10',
    }
    # 处理data数据
    data = urllib.parse.urlencode(data).encode('utf8')
    # 
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    request = urllib.request.Request(url=post_url, headers=headers)
    
    response = urllib.request.urlopen(request, data=data)
    
    print(response.read().decode())
    

    2、复杂get

    百度贴吧
    • https://tieba.baidu.com/f?kw=%E6%9D%8E%E6%AF%85&ie=utf-8&pn=0
      第一页 pn=0
      第二页 pn=50
      第三页 pn=100
      第n页 pn=(n-1)*50
      输入吧名,输入要爬取的起始页码,输入要爬取的结束页码
      在当前创建一个文件夹,文件夹名字就是吧名,在吧名文件夹里面,有好多文件,就每一页的内容,第1页.html 第2页.html xxxx
    import urllib.request
    import urllib.parse
    import os
    import time
    
    
    def handle_request(url, baname, page):
       pn = (page-1) * 50
       # 拼接url
       data = {
           'ie': 'utf8',
           'kw': baname,
           'pn': pn,
       }
       query_string = urllib.parse.urlencode(data)
       url += query_string
       # print(url)
       headers = {
           'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
       }
       request = urllib.request.Request(url=url, headers=headers)
    
       return request
    
    # 根据请求对象,下载指定的响应内容到本地
    def download(request, baname, page):
       # 根据吧名,创建文件夹的过程
       # 判断文件夹是否存在,不存在才创建
       if not os.path.exists(baname):
           os.mkdir(baname)
       # 文件的名字
       filename = '第%s页.html' % page
       print('正在下载%s......' % filename)
       # 拼接文件的全路径
       filepath = os.path.join(baname, filename)
    
       response = urllib.request.urlopen(request)
       # 将内容都进来
       content = response.read()
       # 将内容写入到文件中
       with open(filepath, 'wb') as fp:
           fp.write(content)
    
       print('结束下载%s' % filename)
    
    
    def main():
       url = 'https://tieba.baidu.com/f?'
       # 要爬取的贴吧的名字
       baname = input('请输入要爬取的吧名:')
       # 起始页码、结束页码
       start_page = int(input('请输入起始页码:'))
       end_page = int(input('请输入结束页码:'))
       # 搞个循环
       for page in range(start_page, end_page + 1):
           # 根据不同的page生成不同哦url,然后生成不同的请求
           request = handle_request(url, baname, page)
           # 发送请求,获取响应
           download(request, baname, page)
           # 停顿一下
           time.sleep(2)
    
    
    if __name__ == '__main__':
       main()
    


    3、URLError\HTTPError

    • 异常处理,NameError, 这两个异常在urllib.error里面

    • URLError:不存在的域名,电脑没有联网
      www.goudan.com

    • HTTPError: 404找不到页面

    • HTTPError是URLError的子类,如果多个except在进行捕获,HTTPError要写到前面

    import urllib.request
    import urllib.error
    
    '''
    url = 'http://www.maodan.com/'
    
    try:
        response = urllib.request.urlopen(url)
    except Exception as e:
        print(e)
    '''
    
    url = 'https://blog.csdn.net/hudeyu777/article/details/76021574'
    try:
        response = urllib.request.urlopen(url)
    except urllib.error.HTTPError as e:
        print(e)
    except urllib.error.URLError as e:
        print(e)
    
    
    

    4、Handler处理器、自定义Opener

    是什麽意思呢?
    ++++引入请求对象为了定制头部

    更高级的功能就要使用handler和opener,步骤都是首先创建一个handler,然后创建一个opener,然后通过opener的open方法来发送请求

    通过handler和opener实现最基本的请求

    import urllib.request
    
    url = 'http://www.baidu.com/'
    # 创建一个handler
    handler = urllib.request.HTTPHandler()
    # 根据handler来创建一个opener
    opener = urllib.request.build_opener(handler)
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    
    request = urllib.request.Request(url=url, headers=headers)
    # 再往下发送请求操作,都通过opener的open方法,没有urlopen()这个方法了
    response = opener.open(request)
    
    print(response.read().decode('utf8'))
    

    5、代理

    • 代理:代驾,代购,代练
      我们的代理
      代理服务器:
    --------------------西祠代理
    --------------------快代理
    • 浏览器配置代理
      101.236.21.22:8866
    程序配置代理
    import urllib.request
    
    url = 'http://www.baidu.com/s?ie=UTF-8&wd=ip'
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    
    request = urllib.request.Request(url, headers=headers)
    
    # 101.236.21.22:8866
    # 使用代理访问
    handler = urllib.request.ProxyHandler(proxies={'http': '101.236.21.22:8866'})
    # 根据handler创建opener
    opener = urllib.request.build_opener(handler)
    
    response = opener.open(request)
    
    with open('daili.html', 'wb') as fp:
        fp.write(response.read())
    
    

    6、cookie

    cookie是什麽?会员卡

    为什么引入cookie?http有一个特点,无状态

    每次的请求和响应都是独立的,没有什么关系
    缺点?
    两次请求如果有联系,服务端识别不出来
    要想识别出来,引入了cookie

    day03-爬虫3

    1、cookie

    常用在模拟登录中

    通过代码如何访问登录后的页面?

    http://www.renren.com/960481378/profile

    • (1)伪造cookie,进行访问
    • (2)模拟浏览器的方式,首先发送post,先登录成功,然后发送get,访问登录后的页面
      ps:fiddler上面,一个本上面有个箭头,这个就是post请求
    import urllib.request
    import urllib.parse
    
    
    # 直接访问这个页面,肯定是不行的,需要伪造cookie
    url = 'http://www.renren.com/960481378/profile'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    request = urllib.request.Request(url=url, headers=headers)
    
    response = urllib.request.urlopen(request)
    
    with open('renren.html', 'wb') as fp:
        fp.write(response.read())
    
    
    
    # 通过抓包,抓取浏览器访问时候的cookie,通过代码模拟发送即可
    
    url = 'http://www.renren.com/960481378/profile'
    headers = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Connection': 'keep-alive',
        'Cookie': 'anonymid=jjgfrq7uasf0qc; depovince=GW; jebecookies=49de5f46-0b80-490f-9277-f19688fc91a3|||||; _r01_=1; JSESSIONID=abcYl2ngsw4FBhTX16gsw; ick_login=52175eb0-6f30-4f87-b614-91fa81350f73; _de=F872F5698F7602B30ADE65415FC01940; p=42e7d8c2c4c06f39f70b2be38468f15f8; first_login_flag=1; ln_uact=17701256561; ln_hurl=http://head.xiaonei.com/photos/0/0/men_main.gif; t=d34342f75a43ffa1c061400e9126ea118; societyguester=d34342f75a43ffa1c061400e9126ea118; id=960481378; xnsid=79143e47; ver=7.0; loginfrom=null; wp_fold=0; jebe_key=2f649712-9cf8-44a8-8c6a-145cfab423ae%7C86ba94a3b75a9848502e25ac92562959%7C1531272254868%7C1',
        'Host': 'www.renren.com',
        'Referer': 'http://www.renren.com/960481378/profile',
        'Upgrade-Insecure-Requests': '1',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    request = urllib.request.Request(url=url, headers=headers)
    
    response = urllib.request.urlopen(request)
    
    with open('renren.html', 'wb') as fp:
        fp.write(response.read())
    
    
    模拟登陆人人网获取登陆后的界面
    import urllib.request
    import urllib.parse
    import http.cookiejar
    
    # 高级请求cookie的代码实现
    # 这个ck对象可以保存cookie
    ck = http.cookiejar.CookieJar()
    # 根据ck对象创建一个handler
    handler = urllib.request.HTTPCookieProcessor(ck)
    # 根据handler创建一个opener
    opener = urllib.request.build_opener(handler)
    
    # 往下所有的请求都使用opener.open()方法进行发送,这样就会和浏览器的功能一模一样,就是保存cookie并且自动携带cookie发送请求的功能
    
    # 抓包获取到post_url
    post_url = 'http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=201863950868'
    # 表单数据
    data = {
        'email': '17701256561',
        'icode': '',
        'origURL': 'http://www.renren.com/home',
        'domain': 'renren.com',
        'key_id': '1',
        'captcha_type': 'web_login',
        'password': '776f67653bd0b50d6159b7b5173b74249b9e0765da701ff559c986054b9871a7',
        'rkey': 'fe41ddb7ec32db83d8bdbcc6945e267a',
        'f': 'http%3A%2F%2Fwww.renren.com%2F960481378%2Fprofile',
    }
    data = urllib.parse.urlencode(data).encode('utf8')
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    
    request = urllib.request.Request(url=post_url, headers=headers)
    
    response = opener.open(request, data=data)
    
    
    print(response.read().decode('utf8'))
    # with open('renren.html', 'wb') as fp:
    #   fp.write(response.read())
    
    
    # 访问登录后的页面
    url = 'http://www.renren.com/960481378/profile'
    headers1 = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    request = urllib.request.Request(url=url, headers=headers1)
    
    response = opener.open(request)
    
    with open('renren.html', 'wb') as fp:
        fp.write(response.read())
    

    2、正则表达式

    为什么引入正则表达式?

    • 字符串,处理函数也非常重要,比如如果要去一个文件查找所有的邮箱
      test@163.com
      889910@qq.com

    • 正则表达式。可以匹配一类的东西,肯定有好多规则

    • 世界上最难懂四种东西。女人的心,医生的处方、道士的符、程序媛的正则

    正则表达式规则

    1.单字符匹配:

    . : 除了换行符以外所有的字符

    \d : 匹配 0-9 [0-9] [1-5]

    \D: 除了\d

    \w : 数字、字母、下划线 [a-zA-Z0-9_]

    \W : 除了\w

    \s : 所有的空白字符,tab、空格 。。。

    \S : 除了\s

    [aeiou] : 匹配中括号里面任意个

    2.数量修饰:

    {5} : 修饰前面的字符出现5次

    {5,8} : 5-8次

    a{5,8}

    aaaaaaaa 贪婪的

    {5,} : 最少是5次

    {0,} : 任意多次 *

    {1,} : 至少1次 +

    {0,1} : 可有可无 ?

    python模块 re

    re.match(): >>>> 从头开始匹配,匹配成功就返回
    re.search() >>>> 从任意位置开始匹配,匹配成功返回 只找一个
    返回的是对象, >>>> obj.group()
    re.findall() :>>>>>查找所有符合要求的字符串
    返回的是列表,列表里面都是匹配成功的字符串

    3.边界:

    ^ : 以xxx开头

    $ : 以xxx结尾

    4.分组:

    a(bc){5}

    小括号作用
    • (1)视为一个整体
    • (2)子模式
    贪婪模式:
    .*
    .+
    .*? 取消贪婪
    .+? 取消贪婪
    模式修正
    re.I : 忽略大小写
    re.M : 视为多行模式
    re.S : 视为单行模式
    
    import re
    
    # 创建正则对象
    
    pattern = re.compile(r'\d{3,5}')
    string = '我以前总共谈过1234567次恋爱,我总共结婚7654321次'
    
    # ret = pattern.match(string)
    # ret = pattern.search(string)
    ret = pattern.findall(string)
    
    print(ret)
    
    
    
    pattern = re.compile(r'a(bc){3}')
    string = '哈哈呵呵abcbcbcbcbc嘻嘻嘿嘿么么'
    
    ret = pattern.search(string)
    
    print(ret.group())
    
    
    
    string = '哈哈<div><span>醉卧沙场君莫笑,古来征战几人回</span></div>嘻嘻'
    pattern = re.compile(r'<(\w+)><(\w+)>.+</\2></\1>')
    
    ret = pattern.search(string)
    
    print(ret.group())
    
    
    
    string = '<div>两岸猿声啼不住,轻舟已过万重山</div></div></div>'
    
    pattern = re.compile(r'<div>.*?</div>')
    
    ret = pattern.search(string)
    
    print(ret.group())
    
    
    """
    string = '''english
    love is a forever problem
    love is feel
    '''
    
    pattern = re.compile(r'^love', re.M)
    
    ret = pattern.search(string)
    print(ret.group())
    """
    
    """
    string = '''<div>细思极恐
    你的对手在看书
    你的敌人在磨刀
    你的闺蜜在减肥
    隔壁老王在练腰
    </div>
    '''
    pattern = re.compile(r'<div>.*</div>', re.S)
    ret = pattern.search(string)
    print(ret.group())
    """
    
    def fn(ret):
        number = int(ret.group())
        number += 1
    
        return str(number)
    
    # 正则替换
    string = '男人都喜欢19岁的女孩'
    
    pattern = re.compile(r'\d+')
    
    # ret = pattern.sub('40', string)
    ret = pattern.sub(fn, string)
    
    print(ret)
    
    

    糗事百科图片--实战

    import urllib.request
    import urllib.parse
    import re
    import os
    import time
    
    def handle_request(url, page):
        url += str(page) + '/'
        # print(url)
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
        }
        request = urllib.request.Request(url=url, headers=headers)
        return request
    
    def parse_content(request):
        response = urllib.request.urlopen(request)
        content = response.read().decode('utf8')
        with open('lala.html', 'w', encoding='utf8') as fp:
            fp.write(content)
        # 解析内容,提取这一页所有的图片链接
        pattern = re.compile(r'<div class="thumb">.*?<a href=".*?" target="_blank">.*?<img src="(.*?)" alt="(.*?)" />.*?</div>', re.S)
    
        ret = pattern.findall(content)
        # print(ret)
        # exit()
        # print(len(ret))
        download(ret)
    
    def download(ret):
        for image_info in ret:
            # 取出图片的链接
            image_src = image_info[0]
            # 取出图片的标题
            image_alt = image_info[1]
            # 拼接完整的url
            image_src = 'https:' + image_src
            dirname = 'tupian'
            # 创建文件夹
            if not os.path.exists(dirname):
                os.mkdir(dirname)
            # 得到后缀名
            suffix = image_src.split('.')[-1]
            # 文件名字
            filename = image_alt + '.' + suffix
            filepath = os.path.join(dirname, filename)
    
            print('正在下载%s......' % filename)
            # 下载图片
            urllib.request.urlretrieve(image_src, filepath)
            print('结束下载%s' % filename)
            time.sleep(3)
    
    
    def main():
        start_page = int(input('请输入要爬取的起始页码:'))
        end_page = int(input('请输入要爬取的结束页码:'))
        url = 'https://www.qiushibaike.com/pic/page/'
        for page in range(start_page, end_page + 1):
            print('正在下载第%s页......' % page)
            # 拼接url。构建请求对象
            request = handle_request(url, page)
            # 发送请求,获取响应,并且解析内容
            parse_content(request)
            print('结束下载第%s页' % page)
            time.sleep(3)
    
    if __name__ == '__main__':
        main()
    
    

    励志语句--实战

    import urllib.request
    import urllib.parse
    import re
    import time
    
    
    def main():
        url = 'http://www.yikexun.cn/lizhi/qianming/list_50_{}.html'
        start_page = int(input('请输入起始页码:'))
        end_page = int(input('请输入结束页码:'))
        # 在这里打开文件
        fp = open('lizhi.html', 'w', encoding='utf8')
        for page in range(start_page, end_page + 1):
            print('正在爬取第%s页......' % page)
            # 拼接url,生成请求对象
            request = handle_request(url, page)
            content = urllib.request.urlopen(request).read().decode('utf8')
            # 解析内容函数
            parse_content(content, fp)
            print('结束爬取第%s页' % page)
            time.sleep(2)
        fp.close()
    
    def handle_request(url, page=None):
        if page:
            url = url.format(page)
        # print(url)
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
        }
        return urllib.request.Request(url=url, headers=headers)
    
    def parse_content(content, fp):
        # 写正则表达式,提取所有的标题和链接
        pattern = re.compile(r'<h3><a href="(/lizhi/qianming/\d+\.html)">(.*?)</a></h3>')
        ret = pattern.findall(content)
    
        # print(ret)
        # print(len(ret))
        i = 0
        for href_title in ret:
            # 取出链接
            href = 'http://www.yikexun.cn' + href_title[0]
            # 取出标题
            title = href_title[1]
            print('正在爬取%s......' % title)
            # 因为只有第一个title两边有b标签,所以这里写了一个小小的判断,只在循环第一次执行
            if i == 0:
                # 将title两边的b标签干掉
                title = title.strip('<>b/')
                i += 1
            # print(title)
            # 获取内容的函数
            text = get_text(href)
            # 将标题和内容写入到文件中
            string = '<h1>%s</h1>%s' % (title, text)
            fp.write(string)
            print('结束爬取%s...' % title)
    
            time.sleep(2)
    
    def get_text(href):
        # 构建请求对象,发送请求,解析响应,返回解析后的内容
        request = handle_request(href)
        content = urllib.request.urlopen(request).read().decode('utf8')
        pattern = re.compile(r'<div class="neirong">(.*?)</div>', re.S)
        ret = pattern.search(content)
        # print(ret.group(1))
        # exit()
        text = ret.group(1)
        # 将图片链接去除掉
        pattern = re.compile(r'<img .*?>')
        text = pattern.sub('', text)
        return text
    
    if __name__ == '__main__':
        main()
    

    day04-爬虫4

    1、bs4语法

    是什麽?
    BeautifulSoup,就是一个第三方的库,使用之前需要安装
    • pip install bs4

    • pip进行安装,默认是从国外安装,所以需要将pip源设置为国内源,国内有豆瓣源、阿里源、网易源等等xxx

    配置永久国内源:

    • 1.windows配置方式:

    (1)打开文件资源管理器
    --------在地址栏中输入 %appdata%
    (2)手动创建一个文件夹叫做 pip
    (3)在pip的文件夹里面新建一个文件 pip.ini
    (4)文件中写如下内容

    [global]
    index-url = http://pypi.douban.com/simple
    [install]
    trusted-host=pypi.douban.com
    
    • linux配置方式:

    (1)cd ~
    (2)mkdir .pip
    (3)vi ~/.pip/pip.conf
    (4)编辑内容和windows的内容一模一样

    pip install bs4 pip install lxml
    bs4是什麽?
    • 它的作用是能够快速方便简单的提取网页中指定的内容,给我一个网页字符串,然后使用它的接口将网页字符串生成一个对象,然后通过这个对象的方法来提取数据
    lxml是什麽?
    • lxml是一个解析器,也是下面的xpath要用到的库,bs4将网页字符串生成对象的时候需要用到解析器,就用lxml,或者使用官方自带的解析器 html.parser

    bs4语法学习

    • 通过本地文件进行学习,通过网络进行写代码
      (1)根据标签名进行获取节点
      只能找到第一个符合要求的节点
      (2)获取文本内容和属性
    • 属性

    soup.a.attrs 返回一字典,里面是所有属性和值
    soup.a['href'] 获取href属性

    • 文本

    soup.a.string
    soup.a.text
    soup.a.get_text()
    【注】当标签里面还有标签的时候,string获取的为None,其他两个获取纯文本内容

    (3)find方法

    soup.find('a')
    soup.find('a', class_='xxx')
    soup.find('a', title='xxx')
    soup.find('a', id='xxx')
    soup.find('a', id=re.compile(r'xxx'))
    【注】find只能找到符合要求的第一个标签,他返回的是一个对象

    (4)find_all

    返回一个列表,列表里面是所有的符合要求的对象
    soup.find_all('a')
    soup.find_all('a', class_='wang')
    soup.find_all('a', id=re.compile(r'xxx'))
    soup.find_all('a', limit=2) 提取出前两个符合要求的a

    (5)select

    选择,选择器 css中
    常用的选择器
    标签选择器、id选择器、类选择器
    层级选择器**
    div h1 a 后面的是前面的子节点即可
    div > h1 > a 后面的必须是前面的直接子节点
    属性选择器
    input[name='hehe']
    select('选择器的')
    【注】返回的是一个列表,列表里面都是对象
    【注】find find_all select不仅适用于soup对象,还适用于其他的子对象,如果调用子对象的select方法,那么就是从这个子对象里面去找符合这个选择器的标签

    from bs4 import BeautifulSoup
    
    # 生成soup对象
    soup = BeautifulSoup(open('soup.html', encoding='utf8'), 'lxml')
    print(type(soup))
    
    print(soup.a.attrs)
    print(soup.a.attrs['href'])
    print(soup.a['href'])
    
    print(soup.a.string)
    print(soup.a.text)
    print(soup.a.get_text())
    
    print(soup.div.string)
    print(soup.div.text)
    print(soup.div.get_text())
    
    ret = soup.find('a')
    ret = soup.find('a', title='清明')
    ret = soup.find('a', class_='dumu')
    print(ret)
    
    import re
    ret = soup.find('a', id='xiaoge')
    ret = soup.find('a', id=re.compile(r'^x'))
    print(ret.string)
    
    ret = soup.find_all('a')
    print(ret[1].string)
    
    ret = soup.find_all('a', class_='wang')
    print(ret)
    
    ret = soup.find_all('a', id=re.compile(r'^x'))
    print(ret)
    
    ret = soup.select('a')
    ret = soup.select('#muxiong')
    print(ret[0]['title'])
    
    ret = soup.select('.wang')
    print(ret)
    
    ret = soup.select('div > a')
    print(ret)
    
    ret = soup.select('a[title=东坡肉]')
    
    print(ret)
    
    odiv = soup.select('.tang')[0]
    
    ret = odiv.select('a')
    
    print(ret)
    

    滚滚长江东逝水,浪花淘尽英雄,是非成败转头看,青山依旧在,几度夕阳红
    白发渔樵江渚上,观看秋月春风,一壶浊酒喜相逢,古今多少事,都付笑谈中

    三国演义--实战

    import urllib.request
    import urllib.parse
    from bs4 import BeautifulSoup
    import time
    
    # 给我一个url,返回一个请求对象
    def handle_request(url):
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
        }
        return urllib.request.Request(url=url, headers=headers)
    
    def parse_first_page(url):
        request = handle_request(url)
        # 发送请求,获取响应
        content = urllib.request.urlopen(request).read().decode('utf8')
        time.sleep(2)
        # 使用bs解析内容,生成soup对象
        soup = BeautifulSoup(content, 'lxml')
        # print(soup)
        # 查找所有的章节链接对象
        a_list = soup.select('.book-mulu > ul > li > a')
        # print(ret)
        # print(len(ret))
        # 打开文件
        fp = open('三国演义.txt', 'w', encoding='utf8')
        # 遍历所有的a对象
        for oa in a_list:
            # 取出这个a的内容
            title = oa.string
            # 取出a的href属性
            href = 'http://www.shicimingju.com' + oa['href']
    
            print('正在下载%s......' % title)
            # 向href发送请求,直接获取到解析之后的内容
            neirong = get_neirong(href)
            print('结束下载%s' % title)
            time.sleep(2)
            string = '%s\n%s' % (title, neirong)
            # 将string写入到文件中
            fp.write(string)
    
        fp.close()
    
    def get_neirong(href):
        # 向href发送请求,获取响应,解析响应,返回内容
        request = handle_request(href)
        content = urllib.request.urlopen(request).read().decode('utf8')
        # 生成soup对象,提取章节内容
        soup = BeautifulSoup(content, 'lxml')
        # 找包含章节内容的div
        odiv = soup.find('div', class_='chapter_content')
        neirong = odiv.text
        return neirong
    
    def main():
        url = 'http://www.shicimingju.com/book/sanguoyanyi.html'
        # 解析第一个页面,返回所有的章节列表
        chapter_list = parse_first_page(url)
    
    if __name__ == '__main__':
        main()
    

    智联招聘--实战

    import urllib.request
    import urllib.parse
    import time
    from bs4 import BeautifulSoup
    
    class ZhiLianSpider(object):
        def __init__(self, jl, kw, start_page, end_page):
            # 保存到成员属性中,这样在其他的方法中就可以直接使用
            self.jl = jl
            self.kw = kw
            self.start_page = start_page
            self.end_page = end_page
    
            self.items = []
    
        def handle_request(self, page):
            url = 'https://sou.zhaopin.com/jobs/searchresult.ashx?'
            data = {
                'jl': self.jl,
                'kw': self.kw,
                'p': page,
            }
            query_string = urllib.parse.urlencode(data)
            # 拼接url
            url += query_string
            # print(url)
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
            }
            return urllib.request.Request(url=url, headers=headers)
    
        def parse_content(self, content):
            # 生成soup对象
            soup = BeautifulSoup(content, 'lxml')
            # 首先找到所有的table
            table_list = soup.find_all('table', class_='newlist')[1:]
            # print(table_list)
            # print(len(table_list))
            # 遍历所有的table,依次提取每一个工作的信息
            for table in table_list:
                # 职位名称
                zwmc = table.select('.zwmc a')[0].text.strip('\xa0')
                # 公司名称
                gsmc = table.select('.gsmc a')[0].text
                # 职位月薪
                zwyx = table.select('.zwyx')[0].text
                # 工作地点
                gzdd = table.select('.gzdd')[0].text
                # print(gzdd)
                # exit()
                item = {
                    '职位名称': zwmc,
                    '公司名称': gsmc,
                    '职位月薪': zwyx,
                    '工作地点': gzdd,
                }
                self.items.append(item)
    
        def run(self):
            # 搞个循环
            for page in range(self.start_page, self.end_page + 1):
                print('正在爬取第%s页......' % page)
                # 拼接url的过程,构建请求对象
                request = self.handle_request(page)
                content = urllib.request.urlopen(request).read().decode('utf8')
                # 给我请求对象,解析并且提取内容
                self.parse_content(content)
                print('结束爬取第%s页' % page)
                time.sleep(2)
    
            # 将所有的工作保存到文件中
            string = str(self.items)
            with open('work.txt', 'w', encoding='utf8') as fp:
                fp.write(string)
    
    def main():
        # 输入工作地点
        jl = input('请输入工作地点:')
        # 输入工作关键字
        kw = input('请输入关键字:')
        # 输入起始页码
        start_page = int(input('请输入起始页码:'))
        # 输入结束页码
        end_page = int(input('请输入结束页码:'))
        zhilian = ZhiLianSpider(jl, kw, start_page, end_page)
        zhilian.run()
    
    if __name__ == '__main__':
        main()
    

    2、xpath语法

    xml : 和json是一样的,用在数据交互和传输中,但是到现在用的基本上都是json格式
    为什么使用json格式?因为js原生支持

    xpath : 就是用来解析xml数据的
    因为这个xml和html很像,所以在python里面有一个第三方库 lxml,提供了一些xpath可以解析html的一些函数,你可以直接使用

    pip install lxml

    xpath简单语法:

    / ----------根节点开始查找
    // --------从任意位置开始查找
    . ---------从当前节点开始查找
    .. -----从上一级节点开始查找
    @ -----------选取属性

    bookstore/book :查找bookstore这个节点下面所有的book,直接子节点
    //book : 在整个文档中查找所有的book
    bookstore//book :查找bookstore下面所有的book
    //@lang :-查找所有拥有lang属性的节点
    bookstore/book[1] :查找第一个book,下标从1开始
    bookstore/book[last()] :-最后一个book
    bookstore/book[last()-1] : 倒数第二个
    //title[@lang] :所有的有lang属性的title
    //title[@lang='eng'] :所有的lang属性的值为eng的title节点

    day05-爬虫5

    1、xpath使用

    安装xpath插件

    • 启动和关闭插件: ctrl + shift + x

    常用的xpath

    (1)属性定位

    • 查找id=kw的input框

    //input[@id="kw"]

    (2)层级和索引定位

    //div[@id="head"]/div/div[3]/a[2]

    (3)模糊匹配

    contains
    class属性包含av的所有a
    //a[contains(@class,"av")]

    内容包含产品的所有a
    //a[contains(text(),"产品")]

    starts_with
    starts-with
    class以m开头的所有a
    //a[starts-with(@class,"m")]

    内容以新开头的所有a
    //a[starts-with(text(),"新")]

    (4)取文本和属性

    • 找内容以新开头的所有a标签的文本内容

    //a[starts-with(text(),"新")]/text()

    • 找内容以新开头的所有a标签的href属性

    //a[starts-with(text(),"新")]/@href

    代码中操作xpath

    步骤:给一个网页字符串,会将网页字符串生成对象,然后根据对象的xpath方法去找指定的节点即可

    from lxml import etree
    
    # 将本地的文件生成tree对象
    tree = etree.parse('xpath.html')
    
    # ret = tree.xpath('//li[@id="fei"]')
    # ret = tree.xpath('//div[@class="mingju"]/ul/li[2]/a')
    # ret = tree.xpath('//li[contains(text(),"花")]')
    # ret = tree.xpath('//a[starts-with(@class,"y")]/text()')
    # ret = tree.xpath('//a[starts-with(@class,"y")]/@href')
    
    ret = tree.xpath('//div[@id="xing"]//text()')
    string = '-'.join(ret).replace('\n', '').replace('\t', '')
    
    print(string)
    

    本地测试

    【注】获取标签里面还有标签的内容的时候
    obj/text() 只能找到本标签里面的内容,返回的都是列表,列表需要自己处理
    obj//text() 标签里面所有的内容,返回的是列表,列表需要处理

    下载图片例子

    sc.chinaz.com
    http://sc.chinaz.com/tag_tupian/YaZhouMeiNv.html 第一页
    http://sc.chinaz.com/tag_tupian/yazhoumeinv_2.html 第二页

    from lxml import etree
    import time
    import urllib.request
    import urllib.parse
    import os
    
    def handle_request(url, url_er, page):
        if page == 1:
            url = url
        else:
            url = url_er.format(page)
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
        }
        return urllib.request.Request(url=url, headers=headers)
    
    def parse_content(content):
        # 生成tree对象
        tree = etree.HTML(content)
        # 写xpath,来获取所有的图片的image_src
        image_src_list = tree.xpath('//div[@id="container"]//img/@src2')
        image_alt_list = tree.xpath('//div[@id="container"]//img/@alt')
        # print(image_src_list)
        # print(len(image_src_list))
        for image_src in image_src_list:
            dirname = 'meinv'
            if not os.path.exists(dirname):
                os.mkdir(dirname)
            filename = image_alt_list[image_src_list.index(image_src)]
            suffix = image_src.split('.')[-1]
            filename = filename + '.' + suffix
            filepath = os.path.join(dirname, filename)
            print('正在下载%s。。。。。。' % filename)
            urllib.request.urlretrieve(image_src, filepath)
            print('结束下载%s' % filename)
            time.sleep(2)
    
    def main():
        start_page = int(input('请输入起始页码:'))
        end_page = int(input('请输入结束页码:'))
        url = 'http://sc.chinaz.com/tag_tupian/YaZhouMeiNv.html'
        url_er = 'http://sc.chinaz.com/tag_tupian/yazhoumeinv_{}.html'
        for page in range(start_page, end_page + 1):
            print('正在下载第%s页。。。。。。' % page)
            request = handle_request(url, url_er, page)
            content = urllib.request.urlopen(request).read().decode('utf8')
            # 解析内容函数
            parse_content(content)
            print('结束下载第%s页' % page)
            time.sleep(2)
    
    if __name__ == '__main__':
        main()
    

    懒加载技术

    只显示可视区内的图片,不再可视区内的不显示,等用户滚动滚动条,图片呈现在可视区的时候在显示
    如何实现的?

    <img src='xxx'>
    <img src2='xxx'>  => 出现在可视区,js动态修改为  <img src='xxx'>
    data-original=xxx
    class='lazy'
    data-src='xxx'
    

    json处理

    • 获取豆瓣电影的json数据写入txt
    
    import json
    import urllib.request
    import urllib.parse
    
    url = 'https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&start=20&limit=20'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    request = urllib.request.Request(url=url, headers=headers)
    
    content = urllib.request.urlopen(request).read().decode('utf8')
    
    # print(content)
    # 将json格式数据转化为python对象
    obj = json.loads(content)
    
    # print(type(obj))
    # 现在的obj是一个列表,列表里面都是字典
    lt = []
    # 遍历列表,依次提取每一个字典里面的电影的名字和评分
    for movie in obj:
        title = movie['title']
        score = movie['score']
        # movie['xixi']['hehe']['haha'][0]['lala']
        image_url = movie['cover_url']
        item = {
            '电影名字': title,
            '电影评分': score,
            '电影海报': image_url
        }
        lt.append(item)
    
    string = json.dumps(lt, ensure_ascii=False)
    with open('movie1.txt', 'w', encoding='utf8') as fp:
        fp.write(string)
    

    2、jsonpath

    jsonpath是用来解析json数据的

    格式: {} [] "" , :
    python如何解析json格式

    (1)自带数据类型进行解析

    import json
    json.dumps() : 将python对象转化为json格式字符串
    ensure_ascii=False  写入文件中文显示
    json.loads() : 将json格式字符串转化为python对象
    

    (2)jsonpath解析(了解)

    pip install jsonpath

    http://blog.csdn.net/luxideyao/article/details/77802389
    / $ 根元素
    . @ 当前元素
    / . 层级分隔符
    // .. 任意位置查找

    • jsonpath使用规则
    import json
    import jsonpath
    
    fp = open('book.json', 'r', encoding='utf8')
    string = fp.read()
    fp.close()
    
    # 将json格式字符串转化为python对象
    obj = json.loads(string)
    
    # 使用jsonpath
    # 查找所有book的author节点内容
    # ret = jsonpath.jsonpath(obj, '$.store.book[*].author')
    
    # 从根下面开始找所有的author
    ret = jsonpath.jsonpath(obj, '$..author')
    
    # 查找store下面的所有节点
    ret = jsonpath.jsonpath(obj, '$.store.*')
    
    # 查找store下面的所有price节点
    ret = jsonpath.jsonpath(obj, '$.store..price')
    
    # 查找第三本book
    ret = jsonpath.jsonpath(obj, '$..book[2]')
    
    # 查找最后一本书籍
    ret = jsonpath.jsonpath(obj, '$..book[(@.length-1)]')
    print(ret)
    

    淘宝评论---实战

    用户头像,用户昵称,评论内容,评论图片,评论时间,手机信息
    https://rate.taobao.com/feedRateList.htm?auctionNumId=559141739630&userNumId=100340983&currentPageNum=3&pageSize=20

    import json
    import jsonpath
    import urllib.request
    import urllib.parse
    import time
    
    # 用来存放所有的评论信息
    items = []
    
    def handle_request(page, url):
        url = url.format(page)
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
        }
        return urllib.request.Request(url=url, headers=headers)
    
    def parse_content(content):
        # 如何解析content
        # 处理content,将其转化为合法的json格式字符串
        content = content.strip('() \n\t\r')
        # print(content)
        obj = json.loads(content)
        # print(type(obj))
        # 提取出来所有的评论
        comments_list = obj['comments']
        # 遍历列表,依次提取每一条评论的你要的信息
        for comment in comments_list:
            # 用户头像
            # avatar = comment['user']['avatar']
            avatar = jsonpath.jsonpath(comment, '$..avatar')[0]
            # 用户昵称
            nick = comment['user']['nick']
            # 评论内容
            neirong = comment['content']
            # 图片
            photos_list = comment['photos']
            photos = []
            for photo in photos_list:
                # 获取小图
                little_image = photo['thumbnail']
                # 获取大图
                big_image = photo['url']
                photos.append((little_image, big_image))
            # 时间
            ping_time = comment['date']
            # 手机信息
            info = jsonpath.jsonpath(comment, '$..sku')[0]
            
            item = {
                '用户头像': avatar,
                '用户昵称': nick,
                '评论内容': neirong,
                '晒图': photos,
                '评论时间': ping_time,
                '手机信息': info,
            }
            items.append(item)
    
    def main():
        url = 'https://rate.taobao.com/feedRateList.htm?auctionNumId=559141739630&userNumId=100340983&currentPageNum={}&pageSize=20'
        start_page = int(input('请输入起始页码:'))
        end_page = int(input('请输入结束页码:'))
        for page in range(start_page, end_page + 1):
        # for page in range(1, 2):
            print('正在爬取第%s页......' % page)
            request = handle_request(page, url)
            content = urllib.request.urlopen(request).read().decode('utf8')
            parse_content(content)
            print('结束爬取第%s页......' % page)
            time.sleep(2)
    
        # 爬取完毕, 写入到文件中
        string = json.dumps(items, ensure_ascii=False)
        with open('taobao.json', 'w', encoding='utf8') as fp:
            fp.write(string)
    
    if __name__ == '__main__':
        main()
    
    

    3、selenium+phantomjs

    http://chromedriver.storage.googleapis.com/index.html
    http://blog.csdn.net/huilan_same/article/details/51896672

    day06-爬虫6

    1、selenium+phantomjs

    selenium是什麽?

    是一个浏览器自动化测试工具,自动化就是通过代码操作浏览器,让浏览器自动的做一些操作,是python的第三方库,需要安装才能使用

    pip install selenium

    谷歌驱动下载地址

    http://chromedriver.storage.googleapis.com/index.html

    驱动和浏览器版本关系映射表

    http://blog.csdn.net/huilan_same/article/details/51896672

    selenium 操作谷歌浏览器

    需要有一个谷歌浏览器的驱动,然后操作这个驱动

    from selenium import webdriver
    import time
    
    # 根据webdriver里面的类去创建一个谷歌浏览器对象
    path = r'C:\Users\ZBLi\Desktop\1805\day06\ziliao\chromedriver.exe'
    browser = webdriver.Chrome(path)
    
    # 再往下,操作浏览器就是操作对象
    # 打开百度
    url = 'http://www.baidu.com/'
    browser.get(url)
    time.sleep(2)
    
    # 查找百度输入框
    my_input = browser.find_element_by_id('kw')
    # 写内容
    my_input.send_keys('清纯美女')
    time.sleep(2)
    
    # 查找按钮框
    button = browser.find_element_by_id('su')
    button.click()
    
    time.sleep(3)
    
    # 查找阳光美女链接
    href = browser.find_elements_by_xpath('//div[@class="op-img-covers-divide-high"][2]/a[2]')[0]
    href.click()
    time.sleep(3)
    
    # 推出浏览器
    browser.quit()
    

    find_element_by_id 根据id找固定的节点
    find_elements_by_name 根据name找节点
    find_elements_by_xpath 根据xpath查找
    find_elements_by_tag_name 根据标签名查找
    find_elements_by_class_name 根据类名查找
    find_elements_by_css_selector 根据选择器查找
    find_elements_by_link_text 根据链接内容查找

    作用在哪?

    让selenium操作的是phantomjs

    phantomjs是什麽?

    就是一款浏览器,无界面的浏览器,具有浏览器的功能,html\css\js这些文件浏览器可以直接执行。
    我们关注的就是phantomjs可以执行js文件的功能,在爬取网站的时候,经常碰见动态加载数据的过程。js DOM,解决方法两种

    (1)抓接口,拿出来接口发送请求,解析即可
    (2)让浏览器直接执行js,然后我要执行js之后的网页代码,selenium+phantomjs,终极解决方案,大招,缺点:发请求之后要有停顿

    操作phantomjs

    from selenium import webdriver
    import time
    
    path = r'C:\Users\ZBLi\Desktop\1805\day06\ziliao\phantomjs-2.1.1-windows\bin\phantomjs.exe'
    browser = webdriver.PhantomJS(path)
    
    browser.get('http://www.baidu.com/')
    time.sleep(3)
    
    # 拍照片,记录走到哪了
    browser.save_screenshot(r'tupian\baidu1.png')
    
    browser.find_element_by_id('kw').send_keys('美女')
    time.sleep(2)
    browser.find_element_by_id('su').click()
    time.sleep(3)
    
    browser.save_screenshot(r'tupian\baidu2.png')
    
    browser.quit()
    

    代码中模拟滚动条滚动到底部

    js = 'document.body.scrollTop=10000'
    browser.execute_script(js)

    from selenium import webdriver
    import time
    
    path = r'C:\Users\ZBLi\Desktop\1805\day06\ziliao\phantomjs-2.1.1-windows\bin\phantomjs.exe'
    browser = webdriver.PhantomJS(path)
    
    url = 'https://movie.douban.com/typerank?type_name=%E5%8A%A8%E4%BD%9C&type=5&interval_id=100:90&action='
    browser.get(url)
    time.sleep(3)
    browser.save_screenshot(r'tupian\douban1.png')
    
    # 执行一句js代码即可
    # for x in range(1, 6):
    #   js = 'document.body.scrollTop=10000'
    #   browser.execute_script(js)
    #   time.sleep(3)
    #   browser.save_screenshot(r'tupian\douban2.png')
    
    js = 'document.body.scrollTop=10000'
    browser.execute_script(js)
    time.sleep(3)
    
    # 获取执行js之后的网页代码
    with open(r'tupian\douban.html', 'w', encoding='utf8') as fp:
        fp.write(browser.page_source)
    
    # 往下就可以通过bs或者xpath来解析网页内容了
    
    # 模拟点击加载更多,就是通过browser找到那个按钮,让按钮执行click方法即可
    
    browser.quit()
    

    获取页面的执行js之后的代码

        browser.page_source
    

    2、Headless Chrome

    是什麽?
    无头的谷歌浏览器-无界面的谷歌浏览器,phantomjs现在已经没人维护了,

    要求:windows(60+以上)、linux(59+以上)

    from selenium.webdriver.chrome.options import Options
    chrome_options = Options()
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--disable-gpu')
    
    from selenium import webdriver
    
    import time
    
    from selenium.webdriver.chrome.options import Options
    chrome_options = Options()
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--disable-gpu')
    
    path = r'C:\Users\ZBLi\Desktop\1805\day06\ziliao\chromedriver.exe'
    browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)
    
    browser.get('http://www.baidu.com/')
    time.sleep(2)
    
    browser.save_screenshot(r'tupian\baidu3.png')
    
    browser.quit()
    
    • 当然:不仅谷歌有无界面模式,火狐也有,ie也有,phantomjs

    3、requests

    是什麽?

    • 是一个第三方库,这个库和urllib是一样的,就是模拟浏览器发送http请求的,requests是对urllib的一层封装,所以提供的接口更加的人性化

    详请地址

    http://docs.python-requests.org/zh_CN/latest/index.html

    安装方式

    pip install requests

    get\带参数的get

    data是一个参数字典
    r = requests.get(url=url, params=data)
    响应对象r

    r.text 字符串格式内容
    r.content 字节格式内容
    r.headers 响应头部
    r.url 请求url
    r.status_code 状态码

    
    import requests
    
    '''
    url = 'http://www.baidu.com/'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    
    r = requests.get(url=url, headers=headers)
    '''
    
    # r是响应对象
    # 网页的字符串内容
    # print(r.text)
    # 字节内容
    # print(r.content)
    # 获取网页的url
    # print(r.url)
    # 获取响应头部
    # print(r.headers)
    # 获取状态码
    # print(r.status_code)
    
    url = 'https://www.baidu.com/s?'
    data = {
        'ie': 'utf8',
        'wd': '周杰伦'
    }
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    
    r = requests.get(url=url, params=data, headers=headers)
    
    with open(r'tupian\zhou.html', 'wb') as fp:
        fp.write(r.content)
    

    post

    必应翻译

    data是参数字典
    r = requests.post(url=url, data=data)

    import requests
    
    post_url = 'https://cn.bing.com/ttranslationlookup?&IG=5C360E60322D4FA4865EEBCF710B93B6&IID=translator.5036.2'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    
    fromdata = {
        'from': 'zh-CHS',
        'to': 'en',
        'text': '皇上',
    }
    
    r = requests.post(url=post_url, data=fromdata, headers=headers)
    
    print(r.text)
    

    会话

    登录,人人网---获取保存cooike

    s = requests.Session()
    s.post()
    s.get()

    import requests
    
    # 使用会话技术,首先创建一个会话
    # 往下所有操作,使用s进行发送   s.post  s.get
    s = requests.Session()
    
    post_url = 'http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=2018621432232'
    data = {
        'email':'17701256561',
        'icode':'',
        'origURL':'http://www.renren.com/home',
        'domain':'renren.com',
        'key_id':'1',
        'captcha_type':'web_login',
        'password':'bd20fe8cf1541a10558676a6eeccb4a1a786cfc09823ddd69d5bbaafc7060292',
        'rkey':'227f4ceb2f44827f9de8296ca1ef1c3f',
        'f':'https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DaovDobnt13PO-vgvw1r-eSnSe_QNvNGtexiQFzyME-a%26wd%3D%26eqid%3Db5d58b1e000297f4000000025b4d88e3',
    }
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    
    r = s.post(url=post_url, headers=headers, data=data)
    
    # print(r.text)
    
    url = 'http://www.renren.com/960481378/profile'
    
    r = s.get(url, headers=headers)
    
    with open('renren.html', 'wb') as fp:
        fp.write(r.content)
    
    公交爬取
    import requests
    from lxml import etree
    import re
    import json
    import time
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    
    def parse_first_page(url):
        r = requests.get(url=url, headers=headers)
        # 生成一个tree对象
        tree = etree.HTML(r.text)
        # 通过tree对象查找所有的数字、字母链接
        number_href_list = tree.xpath('//div[@class="bus_kt_r1"]/a/@href')
        char_href_list = tree.xpath('//div[@class="bus_kt_r2"]/a/@href')
        return number_href_list + char_href_list
    
    def parse_second_page(url, all_href, fp):
        # 为了拼接完整的url,先将右边的 / 干掉
        url = url.rstrip('/')
        for href in all_href:
            href = url + href
            r = requests.get(href, headers=headers)
            tree = etree.HTML(r.text)
            # 解析,获取所有的公交href信息
            bus_href_list = tree.xpath('//div[@id="con_site_1"]/a/@href')
            bus_name_list = tree.xpath('//div[@id="con_site_1"]/a/text()')
            # print(bus_href_list)
            # exit()
            # 向列表中的url依次发送请求,解析内容
            parse_third_page(url, bus_href_list, bus_name_list, fp)
    
    def parse_third_page(url, bus_href_list, bus_name_list, fp):
        for bus_href in bus_href_list:
            title = bus_name_list[bus_href_list.index(bus_href)]
            print('正在爬取%s......' % title)
            # 拼接完整的url
            bus_href = url + bus_href
            # 向每一路公交的详情页发送请求
            r = requests.get(url=bus_href, headers=headers)
            # 在下面的函数中,解析每一路公交的详细信息
            parse_content(r.text, fp)
            print('结束爬取%s' % title)
            time.sleep(1)
    
    def parse_content(content, fp):
        tree = etree.HTML(content)
        # 获取线路名称
        name = tree.xpath('//div[@class="bus_i_t1"]/h1/text()')[0]
        # 获取运行时间
        runtime = tree.xpath('//div[@class="bus_i_content"]/p[1]/text()')[0]
        # 获取票价信息
        price = tree.xpath('//div[@class="bus_i_content"]/p[2]/text()')[0]
        # 公交公司
        try:
            company = tree.xpath('//div[@class="bus_i_content"]/p[3]/a/text()')[0]
        except Exception as e:
            company = ''
        
        # 更新时间
        gxsj = tree.xpath('//div[@class="bus_i_content"]/p[last()]/text()')[0]
        # 获取公交路线长度
        try:
            length = tree.xpath('//div[@class="bus_label "]/p/text()')[0]
            pattern = re.compile(r'\d+\.\d+')
            ret = pattern.search(length)
            length = ret.group()
        except Exception as e:
            length = ''
        
    
        total_list = tree.xpath('//span[@class="bus_line_no"]/text()')
        # 获取上行总站数, 使用正则将总站数拿走
        pattern = re.compile(r'\d+')
        up_total = total_list[0]
        up_total = pattern.search(up_total).group()
        # 获取下行总站数
        try:
            down_total = total_list[1]
            down_total = pattern.search(down_total).group()
        except Exception as e:
            down_total = ''
        
        # 获取上行的公交站牌信息
        up_site_name = tree.xpath('//div[@class="bus_line_site "][1]//a/text()')
    
        # 获取下行的公交站牌信息
        try:
            down_site_name = tree.xpath('//div[@class="bus_line_site "][2]//a/text()')
        except Exception as e:
            down_site_name = []
        
        
    
        # 将公交的详细信息保存到字典中
        item = {
            '线路名称': name,
            '运行时间': runtime,
            '票价信息': price,
            '公交公司': company,
            '更新时间': gxsj,
            '线路长度': length,
            '上行站数': up_total,
            '上行站牌': up_site_name,
            '下行站数': down_total,
            '下行站牌': down_site_name,
        }
        string = json.dumps(item, ensure_ascii=False)
        fp.write(string + '\n')
    
    
    def main():
        # 打开文件
        fp = open('北京公交路线.txt', 'w', encoding='utf8')
        url = 'http://beijing.8684.cn/'
        # 获取所有的数字、字母链接
        all_href = parse_first_page(url)
        # 遍历列表,依次发送请求,解析二级页面
        parse_second_page(url, all_href, fp)
        fp.close()
    
    if __name__ == '__main__':
        main()
    
    

    4、登录---验证码

    验证码:

    (1)将验证码下载到本地,让用户手动输入
    (2)使用软件识别,效率不高
    (3)使用打码平台,识别率高
    
    import requests
    
    # 搞一个会话
    s = requests.Session()
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }
    
    # 先将验证码下载到本地
    get_url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
    r = s.get(get_url, headers=headers)
    
    # 需要向图片src发送请求,将验证码下载到本地
    image_src = 'https://so.gushiwen.org/RandCode.ashx'
    r = s.get(image_src, headers=headers)
    with open('code.png', 'wb') as fp:
        fp.write(r.content)
    
    
    code = input('请输入验证码:')
    
    post_url = 'https://so.gushiwen.org/user/login.aspx?from=http%3a%2f%2fso.gushiwen.org%2fuser%2fcollect.aspx'
    data = {
        '__VIEWSTATE': 'BvBAwAIKh29BShbC/yKMDsjiElxi+d4wdH3pR2dacgsifqK0rmUzL4Mc9YzHGDc6P6rqB4wMZ39uRj2MpaaSjQtarGnIo6qf1djLGa75XLo/S4b65Uhv2TETKt0=',
        '__VIEWSTATEGENERATOR':'C93BE1AE',
        'from': 'http://so.gushiwen.org/user/collect.aspx',
        'email': '1090509990@qq.com',
        'pwd': '123456',
        'code': code,
        'denglu': '登录',
    }
    
    r = s.post(post_url, headers=headers, data=data)
    
    print(r.text)
    
    

    多进程

    多任务:生活中来看,就是多个任务同时进行,喝酒聊天,开车,手脚并用,唱歌跳舞
    电脑中:录屏、sublime、vnc服务端、浏览器打开等
    代码中:实现多任务,多进程、多线程
    进程:电脑中,每一个软件启动起来都是一个进程,
    代码中:没有运行的时候称之为程序,运行起来之后就是一个进程
    多进程:进程只有一个,称之为主进程,子进程,要实现两个函数同时执行,就要通过主进程来创建子进程
    操作系统实现,只是在进程之间来回切换,切换的非常快,看着像同时执行一样

    如何实现?

    面向过程:(process)

    p = Process(target=xxx, name=xxx, args=(xxx,))
    target: 进程启动之后要执行的函数
    name: 给进程起个名字
    args: 给子进程传递的参数,是一个元组
    p.start() 启动进程
    p.join() 让主进程等待子进程结束
    os.getpid() 获取当前进程id号
    os.getppid() 获取父进程的id号

    from multiprocessing import Process
    import time
    import os
    
    # 想让子进程1执行sing函数
    def sing(a):
        print('接受的参数为%s' % a)
        # 进程id号
        print('进程-%s-开始运行' % os.getpid())
        print('父进程为%s' % os.getppid())
        for x in range(1, 5):
            print('我在唱小情歌')
            time.sleep(1)
    
    # 想让子进程2执行dance函数
    def dance(a):
        print('进程-%s-开始运行' % os.getpid())
        print('父进程为%s' % os.getppid())
        for x in range(1, 5):
            print('我在跳钢管舞')
            time.sleep(1)
    
    def main():
        # 主进程
        print('主进程id号为%s' % os.getpid())
        a = '青花瓷'
        # 创建进程
        p_sing = Process(target=sing, name='唱歌', args=(a,))
        p_dance = Process(target=dance, name='跳舞', args=(a,))
        
        # 启动进程
        p_sing.start()
        p_dance.start()
    
        # 获取进程名字
        print(p_sing.name)
        print(p_dance.name)
    
        # 因为主进程中有子进程的信息,所以主进程必须等子进程结束之后再结束
        p_sing.join()
        p_dance.join()
    
        print('主进程结束')
    
    if __name__ == '__main__':
        main()
    

    面向对象:

    from multiprocessing import Process
    import time
    
    class SingProcess(Process):
        def __init__(self, a):
            # 如果要重写构造方法,一定得手动调用父类的构造方法
            super().__init__()
            self.a = a
    
        def run(self):
            print('传递的参数为%s' % self.a)
            for x in range(1, 5):
                print('我在唱小情歌')
                time.sleep(1)
    
    class DanceProcess(Process):
        def run(self):
            for x in range(1, 5):
                print('我在跳钢管舞')
                time.sleep(1)
            
    
    def main():
        a = '现在很多老歌手为什么不唱歌了'
        p_sing = SingProcess(a)
        # 启动进程,进程启动之后默认执行类里面的run方法
        p_sing.start()
    
        # p_dance = DanceProcess()
        # p_dance.start()
    
        p_sing.join()
        # p_dance.join()
        print('主进程结束')
    
    if __name__ == '__main__':
        main()
    

    多进程拷贝,拷贝文件夹,假如文件夹里面只有文件。假如有100个文件,那么拷贝的时候先拷贝第一个,然后第二个,然后第三个====
    拷贝一个文件就是一个任务,那一共100文件,那难道要开辟100个进程吗?
    进程并不是越多越好,切换占用的时间越大

    练习:多进程拷贝
    拷贝之前记录时间戳,拷贝之后记录时间戳,计算拷贝的时间
    多进程拷贝也是一样,哪个时间短
    引入进程池,规定能创建几个进程,来了任务,5个进程

    进程之间是否共享全局变量

    全局变量
    进程之间是否共享全局变量,不共享
    每一个进程都是单独的代码

    from multiprocessing import Process
    import os
    import time
    
    count = 100
    
    # 该进程用来修改全局变量的值
    def change():
        global count
        count += 100
        print('进程%s修改后的值为%s' % (os.getpid(), count))
    
    # 该进程用来读取全局变量的值
    def read():
        print('进程%s读取的值为%s' % (os.getpid(), count))
    
    def test(c):
        a = 100
        if c == 'hello':
            a += 100
            time.sleep(2)
            print('进程%s修改a的值为%s' % (os.getpid(), a))
        else:
            time.sleep(5)
            print('进程%s读取a的值为%s' % (os.getpid(), a))
    
    def main():
        '''
        p1 = Process(target=change)
        p1.start()
        time.sleep(2)
    
        p2 = Process(target=read)
        p2.start()
        '''
        a = 'hello'
        b = 'world'
        p1 = Process(target=test, args=(a, ))
        p2 = Process(target=test, args=(b, ))
        p1.start()
        p2.start()
    
        p1.join()
        p2.join()
    
    if __name__ == '__main__':
        main()
    

    进程池

    from multiprocessing import Process
    from multiprocessing import Pool
    import os
    import time
    
    def test(name):
        print('任务%s正在运行,进程id号为%s' % (name, os.getpid()))
        time.sleep(2)
    
    def main():
        # 创建一个进程池对象
        po = Pool(3)
    
        # 给进程池添加任务
        lt = ['关羽', '赵云', '张飞', '马超', '黄忠', '吕布', '孙策', '大乔']
        for name in lt:
            po.apply_async(test, args=(name, ))
    
        # 进程池使用完毕之后,关闭
        po.close()
        # 让主进程等待结束
        po.join()
        print('主进程、进程池全部结束')
    
    if __name__ == '__main__':
        main()
    
    

    多线程

    线程:比如qq。比如暴风影音,比如word
    可以同时语音、同时视频、同时聊天,多线程
    暴风影音,视频播放、音频播放,多线程
    word,打字,拼写检查,等,多线程
    多任务的实现:多进程、多线程

    主进程-子进程1-子进程2
    特点:进程之间没有关系,如果一个进程挂了,不影响其它子进程

    进程-主线程-子线程1-子线程2
    特点:线程之间有关系,如果一个线程挂了,整个进程就挂了

    实现方式:(thread)

    面向过程

    t = Thread(target=xxx, name=xxx, args=(xxx,))
    target: 线程启动之后要执行的函数
    name: 线程的名字
    args: 给线程传递的参数
    t.start(): 启动线程
    t.join(): 让主线程等待子线程结束
    threading.current_thread().name : 获取线程名字

    import threading
    import time
    
    def sing(song):
        print('传递过来的参数为%s' % song)
        print('获取线程的名字为%s' % threading.current_thread().name)
        for x in range(1, 5):
            print('我在唱老情歌')
            time.sleep(1)
    
    def dance():
        for x in range(1, 5):
            print('我在跳广场舞')
            time.sleep(1)
    
    def main():
        a = '广岛之恋'
        # 这是一个进程,进程里面有一个主线程,然后主线程创建子线程1(唱歌),子线程2(跳舞)
        t_sing = threading.Thread(target=sing, name='唱歌', args=(a, ))
        t_dance = threading.Thread(target=dance, name='跳舞')
    
        # 启动线程
        t_sing.start()
        t_dance.start()
    
        t_sing.join()
        t_dance.join()
    
        print('主线程、子线程同时结束')
    
    if __name__ == '__main__':
        main()
    

    面向对象

    import threading
    import time
    
    # 滕王阁序  王勃
    # 命硬
    # 沁园春-雪
    # 出师表
    
    class MyThread(threading.Thread):
        def __init__(self, a):
            super().__init__()
            self.a = a
    
        def run(self):
            print('传递过来的参数为%s' % self.a)
            for x in range(1, 5):
                print('凤凰台上凤凰游,凤去台空江自流')
                time.sleep(1)
    
    def main():
        a = '落霞与孤鹜齐飞,秋水共长天一色'
        t = MyThread(a)
        t.start()
        t.join()
    
        print('主线程、子线程全部结束')
    
    if __name__ == '__main__':
        main()
    

    是否共享

    全局变量
    共享全局变量
    局部变量
    不共享局部变量
    线程安全问题
    线程之间可以共享全局变量

    import threading
    import os
    import time
    
    count = 100
    
    # 该线程用来修改全局变量的值
    def change():
        global count
        count += 100
        print('线程%s修改后的值为%s' % (threading.current_thread().name, count))
    
    # 该线程用来读取全局变量的值
    def read():
        print('线程%s读取的值为%s' % (threading.current_thread().name, count))
    
    
    def test():
        a = 100
        name = threading.current_thread().name
        if name == 'lala':
            a += 100
            time.sleep(2)
            print('线程%s修改a的值为%s' % (name, a))
        else:
            time.sleep(5)
            print('线程读取a的值为%s' % a)
    
    def main():
        '''
        t1 = threading.Thread(target=change, name='修改thread')
        t1.start()
        time.sleep(2)
    
        t2 = threading.Thread(target=read, name='读取thread')
        t2.start()
        '''
        t1 = threading.Thread(target=test, name='lala')
        t2 = threading.Thread(target=test)
    
        t1.start()
        t2.start()
    
        t1.join()
        t2.join()
    
    if __name__ == '__main__':
        main()
    

    加锁

    加锁

    lock.acquire()

    释放锁

    lock.release()

    import threading
    import time
    
    count = 100
    # 创建一把锁
    lock = threading.Lock()
    
    def test(number):
        start = time.time()
        # 在这里面修改count的值
        global count
        for x in range(1, 10000000):
            # 加锁
            lock.acquire()
            count += number
            count -= number
            # 释放锁
            lock.release()
        end = time.time()
        # 上厕所,大号,如何解决,加锁
        # 用之前,加锁,用完之后,释放锁
        # 牺牲了效率了
    
        print('循环计算的时间为%s' % (start - end))
    
    def main():
        t1 = threading.Thread(target=test, args=(3, ))
        t2 = threading.Thread(target=test, args=(5, ))
        t2.start()
        t1.start()
        t1.join()
        t2.join()
    
        print('主线程中打印的count的值为%s' % count)
    
    if __name__ == '__main__':
        main()
    

    队列

    队列:买火车票,电动三轮,特点:先进先出
    用在哪?

    线程之间使用队列进行交互,让线程解耦合,生产者-消费者模型

    线程1-生产数据

    交互渠道:队列

    线程2-消费数据
    while 1:
    生产数据
    消费数据

    队列使用:

                from queue import Queue
        q = Queue(5)
        q.put()         添加元素
        q.put(False)    如果队列已满,添加元素立即抛出异常
        q.put(True, 5)  如果队列已满,添加元素5s之后抛出异常
        q.get()         获取元素
        q.get(False)    如果队列为空,获取元素立即抛出异常
        q.get(True, 5)  如果队列为空,获取元素5s之后抛出异常
        q.empty()       队列是否为空
        q.full()        队列是否已满
        q.qsize()       队列长度
    
    from queue import Queue
    
    # 创建一个队列
    # 如果写,代表队列的长度,如果不写,队列长度无限
    q = Queue(5)
    
    print(q.empty())
    print(q.full())
    print(q.qsize())
    
    # 向队列中添加数据
    q.put('吴彦祖')
    q.put('岳云鹏')
    q.put('王宝强')
    q.put('黄渤')
    q.put('刘德华')
    print(q.empty())
    print(q.full())
    print(q.qsize())
    # q.put('古天乐', True, 5)
    
    # 从队列中获取数据
    print(q.get())
    print(q.get())
    print(q.get())
    print(q.get())
    print(q.get())
    # print(q.get(True, 5))
    

    多线程爬虫

    分析:
    爬虫里面如何分多线程,

    循环:
    拼接url,发送请求,获取响应
    解析响应,保存到文件

    涉及到:
    采集线程,3个线程同时采集
    解析线程,3个线程同时解析
    页码队列:里面是要爬取的页码数据
    数据队列:采集线程向队列中添加数据
    解析线程从队列中获取数据
    保存到同一个文件中,锁机制

    import threading
    from queue import Queue
    import time
    from lxml import etree
    import requests
    import json
    
    class CrawlThread(threading.Thread):
        def __init__(self, name, page_queue, data_queue):
            super().__init__()
            self.name = name
            # 保存页码队列
            self.page_queue = page_queue
            self.data_queue = data_queue
            # url
            self.url = 'http://www.fanjian.net/duanzi-{}'
            self.headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
            }
    
        def run(self):
            print('%s线程开始启动' % self.name)
            # 这里面的思路是什么?
            while 1:
                if self.page_queue.empty():
                    break
                # 1、从页码队列中获取页码
                page = self.page_queue.get()
                # 2、将url和页码进行拼接
                url = self.url.format(page)
                # 3、发送请求,获取响应
                r = requests.get(url=url, headers=self.headers)
                time.sleep(1)
    
                # 4、将响应内容放入到数据队列中
                self.data_queue.put(r.text)
                
            print('%s线程结束' % self.name)
    
    class ParseThread(threading.Thread):
        def __init__(self, name, data_queue, lock, fp):
            super().__init__()
            self.name = name
            # 保存数据队列
            self.data_queue = data_queue
    
            self.lock = lock
            self.fp = fp
    
        def run(self):
            print('%s线程开始启动' % self.name)
            # 解析线程解析步骤
            while 1:
    
                if self.data_queue.empty():
    
                    break
                # 1、从数据队列中取出一个数据
                content = self.data_queue.get()
                # 2、解析这个数据
    
                items = self.parse_content(content)
                # 3、写入到文件中
                string = json.dumps(items, ensure_ascii=False)
                # 加锁
                self.lock.acquire()
                self.fp.write(string)
                # 释放锁
                self.lock.release()
            print('%s线程结束' % self.name)
    
        # 解析数据函数
        def parse_content(content):
            # 生成tree对象
            tree = etree.HTML(content)
            # 先找到所有的li标签
            li_list = tree.xpath('//li[@class="cont-item"]')
            items = []
            for oli in li_list:
                # 获取头像
    
                face = oli.xpath('.//div[@class="cont-list-reward"]//img/@data-src')[0]
                # 获取名字
                name = oli.xpath('.//div[@class="cont-list-head"]/a/text()')[0]
                # 获取内容
                text = oli.xpath('.//div[@class="cont-list-main"]/p/text()')[0]
                # 获取时间
                shijian = oli.xpath('.//div[@class="cont-list-info fc-gray"]/text()')[-1]
                item = {
                    '头像': face,
                    '名字': name,
                    '内容': text,
                    '时间': shijian,
                }
    
                # 将字典添加到列表中
                items.append(item)
    
            return items
            
    
    def create_queue():
        page_queue = Queue()
        data_queue = Queue()
        # 向页码队列中添加页码
        for page in range(1, 11):
            page_queue.put(page)
        return page_queue, data_queue
    
    def main():
        # 做什么?
        # 创建锁
        lock = threading.Lock()
        # 打开文件
        fp = open('duanzi.txt', 'w', encoding='utf8')
        # 创建两个队列
        page_queue, data_queue = create_queue()
        # 创建采集、解析线程
        crawlname_list = ['采集线程1', '采集线程2', '采集线程3']
        parsename_list = ['解析线程1', '解析线程2', '解析线程3']
        # 列表,用来保存所有的采集线程和解析线程
        t_crawl_list = []
        t_parse_list = []
        for crawlname in crawlname_list:
            t_crawl = CrawlThread(crawlname, page_queue, data_queue)
            t_crawl.start()
            # 将对应的采集线程保存起来
            t_crawl_list.append(t_crawl)
    
        for parsename in parsename_list:
            t_parse = ParseThread(parsename, data_queue, lock, fp)
            # 将对应的解析线程保存起来
            t_parse_list.append(t_parse)
            t_parse.start()
    
        # 让主线程等待子线程结束之后再结束
        for t_crawl in t_crawl_list:
            t_crawl.join()
        for t_parse in t_parse_list:
            t_parse.join()
    
        fp.close()
        print('主线程、子线程全部结束')
    
    if __name__ == '__main__':
        main()
    
    # 留给大家了,为什么里面没有写数据呢?
    

    自动识别验证码

    (1)光学识别 OCR 其实就是一个软件

    • 别对他期望太高,识别率80% 90%
      *训练它
      代码识别

    pip install pytesseract
    pip install pillow

    转化为灰度图片

    img = img.convert('L')
    img.show()
    

    二值化处理

    threshold = 140
    table = []
    for i in range(256):
        if i < threshold:
            table.append(0)
        else:
            table.append(1)
    out = img.point(table, '1')
    out.show()
    
    img = img.convert('RGB')
    enhancer = ImageEnhance.Color(img)
    enhancer = enhancer.enhance(0)
    enhancer = ImageEnhance.Brightness(enhancer)
    enhancer = enhancer.enhance(2)
    enhancer = ImageEnhance.Contrast(enhancer)
    enhancer = enhancer.enhance(8)
    enhancer = ImageEnhance.Sharpness(enhancer)
    img = enhancer.enhance(20)
    

    实例代码如下

    import pytesseract
    from PIL import Image
    from PIL import ImageEnhance
    
    def shibie(imagepath):
        img = Image.open(imagepath)
    
        img = img.convert('RGB')
        enhancer = ImageEnhance.Color(img)
        enhancer = enhancer.enhance(0)
        enhancer = ImageEnhance.Brightness(enhancer)
        enhancer = enhancer.enhance(2)
        enhancer = ImageEnhance.Contrast(enhancer)
        enhancer = enhancer.enhance(8)
        enhancer = ImageEnhance.Sharpness(enhancer)
        img = enhancer.enhance(20)
    
        # 转化为灰度图片
        img = img.convert('L')
        # img.show()
    
        # 二值化处理
        threshold = 140
        table = []
        for i in range(256):
            if i < threshold:
                table.append(0)
            else:
                table.append(1)
        out = img.point(table, '1')
        # out.show()
    
        img = img.convert('RGB')
    
        return pytesseract.image_to_string(img)
    shibie('code.png')
    

    打码平台,专业,识别率高,花钱

    云打码平台使用

    用户注册(充钱)、开发者注册(创建应用,使用应用识别验证码服务)

    import http.client, mimetypes, urllib, json, time, requests
    
    ######################################################################
    
    class YDMHttp:
    
        apiurl = 'http://api.yundama.com/api.php'
        username = ''
        password = ''
        appid = ''
        appkey = ''
    
        def __init__(self, username, password, appid, appkey):
            self.username = username  
            self.password = password
            self.appid = str(appid)
            self.appkey = appkey
    
        def request(self, fields, files=[]):
            response = self.post_url(self.apiurl, fields, files)
            response = json.loads(response)
            return response
        
        def balance(self):
            data = {'method': 'balance', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey}
            response = self.request(data)
            if (response):
                if (response['ret'] and response['ret'] < 0):
                    return response['ret']
                else:
                    return response['balance']
            else:
                return -9001
        
        def login(self):
            data = {'method': 'login', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey}
            response = self.request(data)
            if (response):
                if (response['ret'] and response['ret'] < 0):
                    return response['ret']
                else:
                    return response['uid']
            else:
                return -9001
    
        def upload(self, filename, codetype, timeout):
            data = {'method': 'upload', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'codetype': str(codetype), 'timeout': str(timeout)}
            file = {'file': filename}
            response = self.request(data, file)
            if (response):
                if (response['ret'] and response['ret'] < 0):
                    return response['ret']
                else:
                    return response['cid']
            else:
                return -9001
    
        def result(self, cid):
            data = {'method': 'result', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'cid': str(cid)}
            response = self.request(data)
            return response and response['text'] or ''
    
        def decode(self, filename, codetype, timeout):
            cid = self.upload(filename, codetype, timeout)
            if (cid > 0):
                for i in range(0, timeout):
                    result = self.result(cid)
                    if (result != ''):
                        return cid, result
                    else:
                        time.sleep(1)
                return -3003, ''
            else:
                return cid, ''
    
        def report(self, cid):
            data = {'method': 'report', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'cid': str(cid), 'flag': '0'}
            response = self.request(data)
            if (response):
                return response['ret']
            else:
                return -9001
    
        def post_url(self, url, fields, files=[]):
            for key in files:
                files[key] = open(files[key], 'rb');
            res = requests.post(url, files=files, data=fields)
            return res.text
    
    ######################################################################
    
    # 用户名
    # 普通账号的用户名
    username    = ''
    
    # 密码
    # 普通账号的密码
    password    = ''                            
    
    # 软件ID,开发者分成必要参数。登录开发者后台【我的软件】获得!
    appid       =                                      
    
    # 软件密钥,开发者分成必要参数。登录开发者后台【我的软件】获得!
    appkey      = ''    
    
    # 图片文件
    filename    = './png/e6ay.png'                        
    
    # 验证码类型,# 例:1004表示4位字母数字,不同类型收费不同。请准确填写,否则影响识别率。在此查询所有类型 http://www.yundama.com/price.html
    codetype    = 1004
    
    # 超时时间,秒
    timeout     = 60                                    
    
    # 检查
    if (username == 'username'):
        print('请设置好相关参数再测试')
    else:
        # 初始化
        yundama = YDMHttp(username, password, appid, appkey)
    
        # 登陆云打码
        uid = yundama.login();
        print('uid: %s' % uid)
    
        # 查询余额
        balance = yundama.balance();
        print('balance: %s' % balance)
    
        # 开始识别,图片路径,验证码类型ID,超时时间(秒),识别结果
        cid, result = yundama.decode(filename, codetype, timeout);
        print('cid: %s, result: %s' % (cid, result))
    
    ######################################################################
    
    

    相关文章

      网友评论

          本文标题:爬虫

          本文链接:https://www.haomeiwen.com/subject/lzlzuftx.html