网络爬虫-02

Spider-01回顾

请求模块(requests)

html = requests.get(url=url,headers=headers).text
html = requests.get(url=url,headers=headers).content.decode('utf-8')

with open('xxx.txt','w',encoding='utf-8') as f:
    f.write(html)

编码模块(urllib.parse)

1、urlencode({dict})
   urlencode({'wd':'美女','pn':'20'})
   编码后 :'wd=%E8%D5XXX&pn=20'

2、quote(string)
   quote('织女')
   编码后 :'%D3%F5XXX'

3、unquote('%D3%F5XXX')

解析模块(re)

  • 使用流程

    p = re.compile('正则表达式',re.S)
    r_list = p.findall(html)
    
  • 贪婪匹配和非贪婪匹配

    贪婪匹配(默认).*
    非贪婪匹配     : .*?
    
  • 正则表达式分组

    1】想要什么内容在正则表达式中加()2】多个分组,先按整体正则匹配,然后再提取()中数据。结果:[(),(),(),(),()]
    

抓取步骤

1】确定所抓取数据在响应中是否存在(右键 - 查看网页源码 - 搜索关键字)
【2】数据存在: 查看URL地址规律
【3】写正则表达式,来匹配数据
【4】程序结构
	a>每爬取1个页面后随机休眠一段时间
# 程序结构
class xxxSpider(object):
    def __init__(self):
        # 定义常用变量,url,headers及计数等
        
    def get_html(self):
        # 获取响应内容函数,使用随机User-Agent
    
    def parse_html(self):
        # 使用正则表达式来解析页面,提取数据
    
    def save_html(self):
        # 将提取的数据按要求保存,csv、MySQL数据库等
        
    def run(self):
        # 程序入口函数,用来控制整体逻辑
        
if __name__ == '__main__':
    # 程序开始运行时间戳
    start = time.time()
    spider = xxxSpider()
    spider.run()
    # 程序运行结束时间戳
    end = time.time()
    print('执行时间:%.2f' % (end-start))

spider-02笔记

数据持久化 - MySQL

  • pymysql回顾

    import pymysql
    
    db = pymysql.connect('localhost','root','123456','maoyandb',charset='utf8')
    cursor = db.cursor()
    
    ins = 'insert into filmtab values(%s,%s,%s)'
    cursor.execute(ins,['霸王别姬','张国荣','1993'])
    
    db.commit()
    cursor.close()
    db.close()
    

数据持久化 - MongoDB

  • MongoDB特点

    1】非关系型数据库,数据以键值对方式存储,端口270172】MongoDB基于磁盘存储
    【3】MongoDB数据类型单一,值为JSON文档,而Redis基于内存,
       3.1> MySQL数据类型:数值类型、字符类型、日期时间类型、枚举类型
       3.2> Redis数据类型:字符串、列表、哈希、集合、有序集合
       3.3> MongoDB数据类型:值为JSON文档
    【4】MongoDB:-> 集合 -> 文档
         MySQL  :->->  表记录
    
  • MongoDB常用命令

    Linux进入: mongo
    >show dbs                  - 查看所有库
    >use 库名                   - 切换库
    >show collections          - 查看当前库中所有集合
    >db.集合名.find().pretty()  - 查看集合中文档
    >db.集合名.count()          - 统计文档条数
    >db.集合名.drop()           - 删除集合
    >db.dropDatabase()         - 删除当前库
    
  • pymongo模块使用

    import pymongo
    
    # 1.连接对象
    conn = pymongo.MongoClient(host = 'localhost',port = 27017)
    # 2.库对象
    db = conn['maoyandb']
    # 3.集合对象
    myset = db['maoyanset']
    # 4.插入数据库
    myset.insert_one({'name':'赵敏'})
    
  • 练习 - 将电影信息存入MongoDB数据库

    """
    猫眼电影top100抓取(电影名称、主演、上映时间)
    存入mongodb数据库中
    """
    import requests
    import re
    import time
    import random
    import pymongo
    
    class MaoyanSpider:
        def __init__(self):
            self.url = 'https://maoyan.com/board/4?offset={}'
            self.headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36'}
            # 三个对象:连接对象、库对象、集合对象
            self.conn = pymongo.MongoClient('127.0.0.1', 27017)
            self.db = self.conn['maoyandb']
            self.myset = self.db['maoyanset2']
    
        def get_html(self, url):
            html = requests.get(url=url, headers=self.headers).text
            # 直接调用解析函数
            self.parse_html(html)
    
        def parse_html(self, html):
            """解析提取数据"""
            regex = '<div class="movie-item-info">.*?title="(.*?)".*?<p class="star">(.*?)</p>.*?<p class="releasetime">(.*?)</p>'
            pattern = re.compile(regex, re.S)
            r_list = pattern.findall(html)
            # r_list: [('活着','牛犇','2000-01-01'),(),(),...,()]
            self.save_html(r_list)
    
        def save_html(self, r_list):
            """数据处理函数"""
            for r in r_list:
                item = {}
                item['name'] = r[0].strip()
                item['star'] = r[1].strip()
                item['time'] = r[2].strip()
                print(item)
                # 存入到mongodb数据库
                self.myset.insert_one(item)
    
        def run(self):
            """程序入口函数"""
            for offset in range(0, 91, 10):
                url = self.url.format(offset)
                self.get_html(url=url)
                # 控制数据抓取频率:uniform()生成指定范围内的浮点数
                time.sleep(random.uniform(0,1))
    
    if __name__ == '__main__':
        spider = MaoyanSpider()
        spider.run()
    

数据持久化 - csv

  • csv描述

    1】作用
       将爬取的数据存放到本地的csv文件中
    
    【2】使用流程
        2.1> 打开csv文件
        2.2> 初始化写入对象
        2.3> 写入数据(参数为列表)3】示例代码
        import csv 
        with open('sky.csv','w') as f:
            writer = csv.writer(f)
            writer.writerow([])
    
  • 示例

    1】题目描述
        创建 test.csv 文件,在文件中写入数据
    
    【2】数据写入 - writerow([])方法
        import csv
        with open('test.csv','w') as f:
    	    writer = csv.writer(f)
    	    writer.writerow(['超哥哥','25'])
    
  • 练习 - 使用 writerow() 方法将猫眼电影数据存入本地 maoyan.csv 文件

    1】在 __init__() 中打开csv文件,因为csv文件只需要打开和关闭1次即可
    【2】在 save_html() 中将所抓取的数据处理成列表,使用writerow()方法写入
    【3】在run() 中等数据抓取完成后关闭文件
    
  • 代码实现

    """
    猫眼电影top100抓取(电影名称、主演、上映时间)
    存入csv文件,使用writerow()方法
    """
    import requests
    import re
    import time
    import random
    import csv
    
    class MaoyanSpider:
        def __init__(self):
            self.url = 'https://maoyan.com/board/4?offset={}'
            self.headers = {'User-Agent':'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; Tablet PC 2.0; .NET4.0E)'}
            # 打开文件,初始化写入对象
            self.f = open('maoyan.csv', 'w', newline='', encoding='utf-8')
            self.writer = csv.writer(self.f)
    
        def get_html(self, url):
            html = requests.get(url=url, headers=self.headers).text
            # 直接调用解析函数
            self.parse_html(html)
    
        def parse_html(self, html):
            """解析提取数据"""
            regex = '<div class="movie-item-info">.*?title="(.*?)".*?<p class="star">(.*?)</p>.*?<p class="releasetime">(.*?)</p>'
            pattern = re.compile(regex, re.S)
            r_list = pattern.findall(html)
            # r_list: [('活着','牛犇','2000-01-01'),(),(),...,()]
            self.save_html(r_list)
    
        def save_html(self, r_list):
            """数据处理函数"""
            for r in r_list:
                li = [ r[0].strip(), r[1].strip(), r[2].strip() ]
                self.writer.writerow(li)
                print(li)
    
        def run(self):
            """程序入口函数"""
            for offset in range(0, 91, 10):
                url = self.url.format(offset)
                self.get_html(url=url)
                # 控制数据抓取频率:uniform()生成指定范围内的浮点数
                time.sleep(random.uniform(1,2))
    
            # 所有数据抓取并写入完成后关闭文件
            self.f.close()
    
    if __name__ == '__main__':
        spider = MaoyanSpider()
        spider.run()
    

汽车之家数据抓取 - 二级页面

  • 领取任务

    1】爬取地址
        汽车之家 - 二手车 - 价格从低到高
        https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp1exx0/2】爬取目标
        所有汽车的 型号、行驶里程、上牌时间、档位、排量、车辆所在地、价格
    
    【3】爬取分析
        *********一级页面需抓取***********
            1、车辆详情页的链接
            
        *********二级页面需抓取***********
            1、名称
            2、行驶里程
            3、上牌时间
            4、档位
            5、排量
            6、车辆所在地
            7、价格
    
  • 实现步骤

    1】确定响应内容中是否存在所需抓取数据 - 存在
    
    【2】找URL地址规律
        第1: https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp1exx0/2: https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp2exx0/
        第n页: https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp{}exx0/3】 写正则表达式
        一级页面正则表达式:<li class="cards-li list-photo-li".*?<a href="(.*?)".*?</li>
        二级页面正则表达式:<div class="car-box">.*?<h3 class="car-brand-name">(.*?)</h3>.*?<ul class="brand-unit-item fn-clear">.*?<li>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<span class="price" id="overlayPrice">(.*?)<b>4】代码实现
    
  • 代码实现

    """
    汽车之家二手车信息抓取
    思路
        1、一级页面:汽车的链接
        2、二级页面:具体汽车信息
    
    建立User-Agent池:防止被网站检测到是爬虫
        使用fake_useragent模块
        安装:sudo pip3 install fake_useragent
        使用:
            from fake_useragent import UserAgent
            UserAgent().random
    """
    import requests
    import re
    import time
    import random
    from fake_useragent import UserAgent
    
    class CarSpider:
        def __init__(self):
            self.url = 'https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp{}exx0/'
    
        def get_html(self, url):
            """功能函数1 - 获取html"""
            headers = { 'User-Agent':UserAgent().random }
            html = requests.get(url=url, headers=headers).text
    
            return html
    
        def re_func(self, regex, html):
            """功能函数2 - 正则解析函数"""
            pattern = re.compile(regex, re.S)
            r_list = pattern.findall(html)
    
            return r_list
    
        def parse_html(self, one_url):
            """爬虫逻辑函数"""
            one_html = self.get_html(url=one_url)
            one_regex = '<li class="cards-li list-photo-li".*?<a href="(.*?)".*?</li>'
            href_list = self.re_func(regex=one_regex, html=one_html)
            for href in href_list:
                two_url = 'https://www.che168.com' + href
                # 获取1辆汽车的具体信息
                self.get_car_info(two_url)
                # 控制爬取频率
                time.sleep(random.randint(1,2))
    
        def get_car_info(self, two_url):
            """获取1辆汽车的具体信息"""
            two_html = self.get_html(url=two_url)
            two_regex = '<div class="car-box">.*?<h3 class="car-brand-name">(.*?)</h3>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<span class="price" id="overlayPrice">¥(.*?)<b>'
            # car_list: [('福睿斯','3万公里','2016年3月','手动 / 1.5L', '廊坊', '5.60'),]
            car_list = self.re_func(regex=two_regex, html=two_html)
            item = {}
            item['name'] = car_list[0][0].strip()
            item['km'] = car_list[0][1].strip()
            item['time'] = car_list[0][2].strip()
            item['type'] = car_list[0][3].split('/')[0].strip()
            item['displace'] = car_list[0][3].split('/')[1].strip()
            item['address'] = car_list[0][4].strip()
            item['price'] = car_list[0][5].strip()
            print(item)
    
        def run(self):
            for i in range(1,5):
                url = self.url.format(i)
                self.parse_html(url)
    
    if __name__ == '__main__':
        spider = CarSpider()
        spider.run()
    
  • 练习 - 将数据存入MySQL数据库

    create database cardb charset utf8;
    use cardb;
    create table cartab(
    name varchar(100),
    km varchar(50),
    years varchar(50),
    type varchar(50),
    displacement varchar(50),
    city varchar(50),
    price varchar(50)
    )charset=utf8;
    
  • 使用redis实现增量爬虫

    """
      提示: 使用redis中的集合,sadd()方法,添加成功返回1,否则返回0
      请各位大佬忽略掉下面代码,自己独立实现
    """
    
    import requests
    import re
    import time
    import random
    import pymysql
    from hashlib import md5
    import sys
    import redis
    
    
    class CarSpider(object):
        def __init__(self):
            self.url = 'https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp{}exx0/'
            self.headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1'}
            self.db = pymysql.connect('localhost','root','123456','cardb',charset='utf8')
            self.cursor = self.db.cursor()
            # 连接redis去重
            self.r = redis.Redis(host='localhost',port=6379,db=0)
    
        # 功能函数1 - 获取响应内容
        def get_html(self,url):
            html = requests.get(url=url,headers=self.headers).text
    
            return html
    
        # 功能函数2 - 正则解析
        def re_func(self,regex,html):
            pattern = re.compile(regex,re.S)
            r_list = pattern.findall(html)
    
            return r_list
    
        # 爬虫函数开始
        def parse_html(self,one_url):
            one_html = self.get_html(one_url)
            one_regex = '<li class="cards-li list-photo-li".*?<a href="(.*?)".*?</li>'
            href_list = self.re_func(one_regex,one_html)
            for href in href_list:
                # 加密指纹
                s = md5()
                s.update(href.encode())
                finger = s.hexdigest()
                # 如果指纹表中不存在
                if self.r.sadd('car:urls',finger):
                    # 每便利一个汽车信息,必须要把此辆汽车所有数据提取完成后再提取下一辆汽车信息
                    url = 'https://www.che168.com' + href
    
                    # 获取一辆汽车的信息
                    self.get_data(url)
                    time.sleep(random.randint(1,2))
                else:
                    sys.exit('抓取结束')
    
        # 判断是否存在:存在返回False,不存在返回True
        def go_spider(self,finger):
            sel = 'select * from request_finger where finger=%s'
            result = self.cursor.execute(sel,[finger])
            if result:
                return False
            return True
    
        # 获取一辆汽车信息
        def get_data(self,url):
            two_html = self.get_html(url)
            two_regex = '<div class="car-box">.*?<h3 class="car-brand-name">(.*?)</h3>.*?<ul class="brand-unit-item fn-clear">.*?<li>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<span class="price" id="overlayPrice">¥(.*?)<b'
            item = {}
            car_info_list = self.re_func(two_regex,two_html)
            item['name'] = car_info_list[0][0]
            item['km'] = car_info_list[0][1]
            item['year'] = car_info_list[0][2]
            item['type'] = car_info_list[0][3].split('/')[0]
            item['displacement'] = car_info_list[0][3].split('/')[1]
            item['city'] = car_info_list[0][4]
            item['price'] = car_info_list[0][5]
            print(item)
    
            one_car_list = [
                item['name'],
                item['km'],
                item['year'],
                item['type'],
                item['displacement'],
                item['city'],
                item['price']
            ]
            ins = 'insert into cartab values(%s,%s,%s,%s,%s,%s,%s)'
            self.cursor.execute(ins,one_car_list)
            self.db.commit()
    
        def run(self):
            for p in range(1,2):
                url = self.url.format(p)
                self.parse_html(url)
    
            # 断开数据库链接
            self.cursor.close()
            self.db.close()
    
    if __name__ == '__main__':
        spider = CarSpider()
        spider.run()
    
  • 爬虫常用插件

    1】google-access-helper : 可访问谷歌应用商店
    【2】Xpath Helper: 轻松获取HTML元素的xPath路径
        打开/关闭: Ctrl + Shift + x
    【3】JsonView: 格式化输出json格式数据
    【4】Proxy SwitchyOmega: Chrome浏览器中的代理管理扩展程序
    

xpath解析

  • 定义

    XPath即为XML路径语言,它是一种用来确定XML文档中某部分位置的语言,同样适用于HTML文档的检索
    
  • 匹配演示 - 猫眼电影top100

    1】查找所有的dd节点
        //dd
    【2】获取所有电影的名称的a节点: 所有class属性值为name的a节点
        //p[@class="name"]/a
    【3】获取dl节点下第2个dd节点的电影节点
        //dl[@class="board-wrapper"]/dd[2]4】获取所有电影详情页链接: 获取每个电影的a节点的href的属性值
        //p[@class="name"]/a/@href
    
    【注意】                             
        1> 只要涉及到条件,[] : //dl[@class="xxx"]   //dl/dd[2]
        2> 只要获取属性值,加 @  : //dl[@class="xxx"]   //p/a/@href
    
  • 选取节点

    1// : 从所有节点中查找(包括子节点和后代节点)
    【2】@  : 获取属性值
      2.1> 使用场景1(属性值作为条件)
           //div[@class="movie-item-info"]
      2.2> 使用场景2(直接获取属性值)
           //div[@class="movie-item-info"]/a/img/@src
        
    【3】练习 - 猫眼电影top100
      3.1> 匹配电影名称
          //div[@class="movie-item-info"]/p[1]/a/@title
      3.2> 匹配电影主演
          //div[@class="movie-item-info"]/p[2]/text()
      3.3> 匹配上映时间
          //div[@class="movie-item-info"]/p[3]/text()
      3.4> 匹配电影链接
          //div[@class="movie-item-info"]/p[1]/a/@href
    
  • 匹配多路径(或)

    xpath表达式1 | xpath表达式2 | xpath表达式3
    
  • 常用函数

    1】text() :获取节点的文本内容
        xpath表达式末尾不加 /text() :则得到的结果为节点对象
        xpath表达式末尾加 /text() 或者 /@href : 则得到结果为字符串
            
    【2】contains() : 匹配属性值中包含某些字符串节点
        匹配class属性值中包含 'movie-item' 这个字符串的 div 节点
         //div[contains(@class,"movie-item")]
    
  • 终极总结

    1】字符串: xpath表达式的末尾为: /text()/@href  得到的列表中为'字符串'2】节点对象: 其他剩余所有情况得到的列表中均为'节点对象' 
        [<element dd at xxxa>,<element dd at xxxb>,<element dd at xxxc>]
        [<element div at xxxa>,<element div at xxxb>]
        [<element p at xxxa>,<element p at xxxb>,<element p at xxxc>]
    
  • 课堂练习

    1】匹配汽车之家-二手车,所有汽车的链接 : 
        //li[@class="cards-li list-photo-li"]/a[1]/@href
        //a[@class="carinfo"]/@href
    【2】匹配汽车之家-汽车详情页中,汽车的
         2.1)名称:  //div[@class="car-box"]/h3/text()
         2.2)里程:  //ul/li[1]/h4/text()
         2.3)时间:  //ul/li[2]/h4/text()
         2.4)挡位+排量: //ul/li[3]/h4/text()
         2.5)所在地: //ul/li[4]/h4/text()
         2.6)价格:   //div[@class="brand-price-item"]/span[@class="price"]/text()
    

lxml解析库

  • 安装

    1】Ubuntu:  sudo pip3 install lxml
    【2】Windows: python -m pip install lxml
    
  • 使用流程

    1、导模块
       from lxml import etree
    2、创建解析对象
       parse_html = etree.HTML(html)
    3、解析对象调用xpath
       r_list = parse_html.xpath('xpath表达式')
    
  • xpath最常用

    1】基准xpath: 匹配所有电影信息的节点对象列表
       //dl[@class="board-wrapper"]/dd
       [<element dd at xxx>,<element dd at xxx>,...]2】遍历对象列表,依次获取每个电影信息
       item = {}
       for dd in dd_list:
    	 	item['name'] = dd.xpath('.//p[@class="name"]/a/text()').strip()
    	 	item['star'] = dd.xpath('.//p[@class="star"]/text()').strip()
    	 	item['time'] = dd.xpath('.//p[@class="releasetime"]/text()').strip()
    
  • 猫眼电影案例-xpath实现

    """
    猫眼电影top100抓取(电影名称、主演、上映时间)
    """
    import requests
    import time
    import random
    from lxml import etree
    
    class MaoyanSpider:
        def __init__(self):
            self.url = 'https://maoyan.com/board/4?offset={}'
            self.headers = {'User-Agent':'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; Tablet PC 2.0; .NET4.0E)'}
    
        def get_html(self, url):
            html = requests.get(url=url, headers=self.headers).text
            # 直接调用解析函数
            self.parse_html(html)
    
        def parse_html(self, html):
            """解析提取数据 - xpath"""
            p = etree.HTML(html)
            # 基准xpath:每个电影信息的节点对象dd列表 [<element dd at xxx>, <element dd at xxx>,...]
            dd_list = p.xpath('//dl[@class="board-wrapper"]/dd')
            print(dd_list)
            item = {}
            for dd in dd_list:
                item['name'] = dd.xpath('.//p[@class="name"]/a/@title')[0].strip()
                item['star'] = dd.xpath('.//p[@class="star"]/text()')[0].strip()
                item['time'] = dd.xpath('.//p[@class="releasetime"]/text()')[0].strip()
                print(item)
    
        def run(self):
            """程序入口函数"""
            for offset in range(0, 91, 10):
                url = self.url.format(offset)
                self.get_html(url=url)
                # 控制数据抓取频率:uniform()生成指定范围内的浮点数
                time.sleep(random.uniform(0,1))
    
    if __name__ == '__main__':
        spider = MaoyanSpider()
        spider.run()
    
  • 小作业

    汽车之家案例使用lxml+xpath实现
    
posted @ 2020-06-03 12:12  野哥李  阅读(11)  评论(0编辑  收藏  举报  来源