python爬虫之requests模块/xpath解析/lxml解析库

文章目录

    • 前情回顾
      • 爬取网站思路
      • 数据持久化 - csv
      • 数据持久化 - MySQL
      • 数据持久化 - MongoDB
      • 多级页面数据抓取
    • 今日笔记
    • 电影天堂二级页面抓取案例
      • 领取任务
      • 实现步骤
    • requests模块
      • 安装
      • requests.get()
    • Chrome浏览器安装插件
      • 安装方法
      • 需要安装插件
    • xpath解析
      • 定义
      • 示例
      • 匹配演示
      • 选取节点
      • 匹配多路径(或)
      • 常用函数
    • lxml解析库
      • 安装
      • 使用流程
      • html样本
      • 示例+练习
      • xpath最常使用方法
    • 链家二手房案例(xpath)
      • 实现步骤
      • 代码实现
    • 任务1 - 猫眼电影数据抓取
    • 任务2 - 百度贴吧图片抓取
      • 目标思路
      • 实现步骤
    • 任务3 - 电影天堂(xpath)

前情回顾

爬取网站思路

1、先确定是否为动态加载网站
2、找URL规律
3、正则表达式
4、定义程序框架,补全并测试代码

数据持久化 - csv

 import csv
 with open('xxx.csv','w') as f:
	writer = csv.writer(f)
 	writer.writerow([])
	writer.writerows([(),(),()])

数据持久化 - MySQL

import pymysql

# __init__(self):
	self.db = pymysql.connect('IP',... ...)
	self.cursor = self.db.cursor()
# write_data(self):
	self.cursor.execute('sql',[data1])
	self.cursor.executemany('sql',[(data1),(data2),(data3)])
	self.db.commit()
# main(self):
	self.cursor.close()
	self.db.close()

数据持久化 - MongoDB

import pymongo

# __init__(self):
	self.conn = pymongo.MongoClient('IP',27017)
	self.db = self.conn['db_name']
	self.myset = self.db['set_name']
	
# write_data(self):
	self.myset.insert_one(dict)
	self.myset.insert_many([{},{},{}])

# MongoDB - Commmand
>show dbs
>use db_name
>show collections
>db.collection_name.find().pretty()
>db.collection_name.count()
>db.collection_name.drop()
>db.dropDatabase()

多级页面数据抓取

# 整体思路 
1、爬取一级页面,提取 所需数据+链接,继续跟进
2、爬取二级页面,提取 所需数据+链接,继续跟进
3... ... 
# 代码实现思路
1、所有数据最终都会在一级页面遍历每条数据时全部拿到
2、避免重复代码 - 请求、解析需定义函数

今日笔记

电影天堂二级页面抓取案例

领取任务

# 地址
电影天堂 - 2019年新片精品 - 更多
# 目标
电影名称、下载链接

# 分析
*********一级页面需抓取***********
        1、电影详情页链接
        
*********二级页面需抓取***********
        1、电影名称
  		  2、电影下载链接

实现步骤

  • 1、确定响应内容中是否存在所需抓取数据
  • 2、找URL规律
1页 :https://www.dytt8.net/html/gndy/dyzz/list_23_1.html
第2页 :https://www.dytt8.net/html/gndy/dyzz/list_23_2.html
第n页 :https://www.dytt8.net/html/gndy/dyzz/list_23_n.html
  • 3、写正则表达式
1、一级页面正则表达式
   <table width="100%".*?<td width="5%".*?<a href="(.*?)".*?ulink">.*?</table>
2、二级页面正则表达式
   <div class="title_all"><h1><font color=#07519a>(.*?)
.*?# 思路 # 1、MySQL中新建表 request_finger,存储所有爬取过的链接的指纹 # 2、在爬取之前,先判断该指纹是否爬取过,如果爬取过,则不再继续爬取

5、代码实现

# 建库建表
create database filmskydb charset utf8;
use filmskydb;
create table request_finger(
finger char(32)
)charset=utf8;
create table filmtab(
name varchar(200),
download varchar(500)
)charset=utf8;
import sys
from urllib import request
import re, time, random, pymysql
from hashlib import md5

from fake_useragent import UserAgent


class FilmSky:
    def __init__(self):
        self.url = 'https://www.ygdy8.net/html/gndy/dyzz/list_23_{}.html'
        self.db = pymysql.connect(
            "127.0.0.1", "root", "123456", "filmskydb", charset="utf8"
        )
        self.cursor = self.db.cursor()

    # 请求
    def get_html(self, url):
        ua = UserAgent()
        headers = {"User-Agent": ua.random}
        req = request.Request(url=url, headers=headers)
        res = request.urlopen(req)
        html = res.read().decode("gb2312", "ignore")
        return html

    # 解析
    def re_func(self, re_db, html):
        pattern = re.compile(re_db, re.S)
        r_list = pattern.findall(html)
        return r_list

    # 提取数据
    def parse_html(self, one_html):
        one_html = self.get_html(one_html)
        # link_list:["/html/xxx","/html/xxx",...]
        # http://www.dytt8.net
        re_db = '.*?
'
link_list = self.re_func(re_db, one_html) # print(link_list) for link in link_list: # 判断是否需要爬取此链接 # 1.获取指纹 two_url = "http://www.ygdy8.net" + link # print(two_url) s = md5() s.update(two_url.encode()) finger = s.hexdigest() # 2.判断指纹是否在数据库中存在 if self.is_go_on(finger): # 1.需要爬取 time.sleep(random.randint(1,2)) self.save_html(two_url) # 2.将该链接存入到数据库 ins = "insert into request_finger values (%s)" # print(ins) self.cursor.execute(ins, [finger]) self.db.commit() else: sys.exit("更新完成!") # 判断是否需要爬取此链接 def is_go_on(self, finger): sel = "select finger from request_finger where finger=%s" # self.cursor.execute(sel,[finger]):返回值为满足此条件的数字 # print(sel) r=self.cursor.execute(sel, [finger]) if not r: # self.cursor.fetchall():获取满足此条件的元组 return True # 真正获取名称和下载链接的函数 def save_html(self, two_url): two_html = self.get_html(two_url) # print(two_html) re_db = '

(.*?)

.*?# film_list:[("name","链接"),]
film_list = self.re_func(re_db, two_html) # print(film_list) # 插入数据库 ins = "insert into filmtab values (%s,%s)" l = list(film_list[0]) print(l) self.cursor.execute(ins, l) self.db.commit() def run(self): for i in range(1, 203): url = self.url.format(i) self.parse_html(url) self.cursor.close() self.db.close() if __name__ == '__main__': movie = FilmSky() movie.run()

requests模块

安装

  • Linux
sudo pip3 install requests
  • Windows
# 方法一
   进入cmd命令行 :python -m pip install requests
# 方法二
   右键管理员进入cmd命令行 :pip install requests

requests.get()

  • 作用
# 向网站发起请求,并获取响应对象
res = requests.get(url,headers=headers)
  • 参数
1、url :需要抓取的URL地址
2、headers : 请求头
3、timeout : 超时时间,超过时间会抛出异常
  • 响应对象(res)属性
1、encoding :响应字符编码
   res.encoding = 'utf-8'
2、text :字符串
3、content :字节流
4、status_code :HTTP响应码
5、url :实际数据的URL地址
  • 非结构化数据保存
with open('xxx.jpg','wb') as f:
	f.write(res.content)
  • 示例

保存赵丽颖图片到本地

import requests

url = "http://img3.imgtn.bdimg.com/it/u=3897002694,3726565855&fm=26&gp=0.jpg"
header = {"User-Agent": "Mozilla/5.0"}

res = requests.get(url=url, headers=header)
# 字符编码
# res.encoding = "utf-8"
# 字符串类型
# url = res.text
# 字节流
content = res.content

with open("lilei.jpg","wb") as f:
    f.write(content)
  • 练习
1、将猫眼电影案例改写为 requests 模块实现
2、将电影天堂案例改写为 requests 模块实现
3、百度图片抓取: 输入要抓取的图片内容,抓取首页的30张图片,保存到对应的文件夹,比如:
   你想要谁的照片,请输入: 赵丽颖
   创建文件夹到指定目录: 赵丽颖  并把首页30张图片保存到此文件夹下

百度图片练习代码实现

import os
import re
import time
import random
from urllib import parse

import requests
from fake_useragent import UserAgent


class BaiduImage:
    def __init__(self):
        self.url = "https://image.baidu.com/search/index?tn=baiduimage&word={}"

    # 随机获取浏览器提示
    def get_ua(self):
        ua = UserAgent()
        agent = ua.random
        return agent

    def get_image(self, url,word):
        html = requests.get(
            url=url,
            headers={"User-Agent": self.get_ua()}
        ).text
        pattern = re.compile('"hoverURL":"(.*?)"', re.S)
        # link_list=["http://xxx.jpg",...]
        link_list = pattern.findall(html)
        # 创建对应的文件夹
        directory='/home/tarena/images/{}/'.format(word)
        if not os.path.exists(directory):
            #如果路径不存在,递归创建文件夹
            os.makedirs(directory)
        i=1
        for link in link_list:
            filename=directory+word+"_"+str(i)+".jpg"
            self.save_image(link,filename)
            time.sleep(random.uniform(1,2))
            i+=1

    def save_image(self, link,filename):
        html=requests.get(url=link,headers={"User-Agent":self.get_ua()}).content
        with open(filename,"wb") as f:
            f.write(html)
            print("%s下载成功"%filename)

    def run(self):
        word=input("请输入图片名:")
        params=parse.quote(word)
        url=self.url.format(params)
        self.get_image(url,word)

if __name__ == '__main__':
    sp=BaiduImage()
    sp.run()

Chrome浏览器安装插件

安装方法

1、把下载的相关插件(对应操作系统浏览器)后缀改为 .zip 
2、解压,打开Chrome浏览器 -> 右上角设置 -> 更多工具 -> 扩展程序 -> 点开开发者模式
#3、把相关插件文件夹拖拽到浏览器中,释放鼠标即可安装
#3、有的插件直接拖拽 .zip 文件释放即可

需要安装插件

1、Xpath Helper: 轻松获取HTML元素的xPath路径
  # 开启/关闭: Ctrl + Shift + x
2、Proxy SwitchyOmega: Chrome浏览器中的代理管理扩展程序
3、JsonView: 格式化输出json格式数据

xpath解析

定义

XPath即为XML路径语言,它是一种用来确定XML文档中某部分位置的语言,同样适用于HTML文档的检索

示例

<ul class="CarList">
	<li class="bjd" id="car_001" href="http://www.bjd.com/">
        <p class="name">布加迪p>
        <p class="model">威航p>
        <p class="price">2500万p>
        <p class="color">红色p>
    li>
    
    <li class="byd" id="car_002" href="http://www.byd.com/">
        <p class="name">比亚迪p>
        <p class="model">p>
        <p class="price">15万p>
        <p class="color">白色p>
    li>
ul>

匹配演示

1、查找所有的li节点
  //li
2、获取所有汽车的名称: 所有li节点下的子节点p的值 (class属性值为name)
  //li/p[@class="name"]  
3、找比亚迪车的信息: 获取ul节点下第2个li节点的汽车信息
  //ul/li[2]                          
4、获取所有汽车的链接: ul节点下所有li子节点的href属性的值
  //ul/li/@href

# 只要涉及到条件,加 []
# 只要获取属性值,加 @

选取节点

1// :从所有节点中查找(包括子节点和后代节点)
2、@  :获取属性值
   # 使用场景1(属性值作为条件)
     //div[@class="movie-item-info"]
   # 使用场景2(直接获取属性值)
     //div[@class="movie-item-info"]/a/img/@src

匹配多路径(或)

xpath表达式1 | xpath表达式2 | xpath表达式3

常用函数

1、contains() :匹配属性值中包含某些字符串节点
   # 查找id属性值中包含字符串 "car_" 的 li 节点
   #
  • #
  • //li[contain(@id,"car_")] 2、text() :获取节点的文本内容 # 查找所有汽车的价格 //li/p[@class="price"]/text() #得到字符串的两种方式:["","",""] 1.text(): //li/p/text() 2.@href: //li/p/@href
  • lxml解析库

    安装

    sudo pip3 install lxml
    

    使用流程

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

    html样本

    <div class="wrapper">
    	<a href="/" id="channel">新浪社会a>
    	<ul id="nav">
    		<li><a href="http://domestic.sina.com/" title="国内">国内a>li>
    		<li><a href="http://world.sina.com/" title="国际">国际a>li>
    		<li><a href="http://mil.sina.com/" title="军事">军事a>li>
    		<li><a href="http://photo.sina.com/" title="图片">图片a>li>
    		<li><a href="http://society.sina.com/" title="社会">社会a>li>
    		<li><a href="http://ent.sina.com/" title="娱乐">娱乐a>li>
    		<li><a href="http://tech.sina.com/" title="科技">科技a>li>
    		<li><a href="http://sports.sina.com/" title="体育">体育a>li>
    		<li><a href="http://finance.sina.com/" title="财经">财经a>li>
    		<li><a href="http://auto.sina.com/" title="汽车">汽车a>li>
    	ul>
    div>
    

    示例+练习

    from lxml import etree
    
    html = '''
    '''
    # 创建解析对象
    parse_html = etree.HTML(html)
    # 调用xpath返回结束,text()为文本内容
    a_list = parse_html.xpath('//a/text()')
    print(a_list)
    
    # 提取所有的href的属性值
    r_list = parse_html.xpath("//div[@class='wrapper']//a/@href | //div[@class='wrapper']//a/@title")
    # 提取所有href的值,不包括 / 
    r_list = parse_html.xpath("//ul[@id='nav']//a/@href | //ul[@id='nav']//a/@title")
    # 获取 图片、军事、...,不包括新浪社会
    r_list = parse_html.xpath("//ul[@id='nav']//a/text()")
    

    xpath最常使用方法

    1、先匹配节点对象列表
      # r_list: ['节点对象1','节点对象2']
      r_list = parse_html.xpath('基准xpath表达式')
    2、遍历每个节点对象,利用节点对象继续调用 xpath
      for r in r_list:
            name = r.xpath('./xxxxxx')
            star = r.xpath('.//xxxxx')
            time = r.xpath('.//xxxxx')
    

    链家二手房案例(xpath)

    实现步骤

    • 确定是否为静态
    打开二手房页面 -> 查看网页源码 -> 搜索关键字
    
    • xpath表达式
    1、基准xpath表达式(匹配每个房源信息节点列表)
       此处滚动鼠标滑轮时,li节点的class属性值会发生变化,通过查看网页源码确定xpath表达式
      //ul[@class="sellListContent"]/li[@class="clear LOGVIEWDATA LOGCLICKDATA"]
    
    2、依次遍历后每个房源信息xpath表达式
       * 名称: './/a[@data-el="region"]/text()'
       
       # 户型+面积+方位+是否精装
       info_list = './/div[@class="houseInfo"]/text()'  [0].strip().split('|')
       * 户型: info_list[1]
       * 面积: info_list[2]
       * 方位: info_list[3]
       * 精装: info_list[4]
       
    
       * 楼层: './/div[@class="positionInfo"]/text()'
       * 区域: './/div[@class="positionInfo"]/a/text()'
       * 总价: './/div[@class="totalPrice"]/span/text()'
       * 单价: './/div[@class="unitPrice"]/span/text()'
    

    代码实现

    import requests, time, random
    from fake_useragent import UserAgent
    from lxml import etree
    
    
    class LianJiaSpider:
        def __init__(self):
            self.url = "https://zz.lianjia.com/ershoufang/pg{}/"
            self.blag=1
    
        # 随机headers
        def get_headers(self):
            agent = UserAgent().random
            headers = {"User-Agent": agent}
            return headers
    
        # 请求
        def get_html(self, url):
            #请求三次.每次请求时间最多为5秒
            if self.blag<=3:
                try:
                    res = requests.get(url=url, headers=self.get_headers(),timeout=5)
                    # html=res.text
                    html = res.content.decode()
                    return html
                except Exception as e:
                    print(e)
                    self.blag+=1
                    self.get_html(url)
    
        # 解析
        def parse_html(self, url):
            html = self.get_html(url)
            if not html:
                return None
            p = etree.HTML(html)
            # 基准xpath表达式 -30个房源节点对象列表
            h_list = p.xpath('//ul[@class="sellListContent"]/li[@class="clear LOGVIEWDATA LOGCLICKDATA"]')
            for h in h_list:
                item = {}
                # 名称
                item["name"] = (h.xpath('.//a[@data-el="region"]/text()')[0] if h.xpath('.//a[@data-el="region"]/text()') else None)
                # 户型+面积+方位+是否精装
                info_list = h.xpath('.//div[@class="houseInfo"]/text()')
                if info_list:
                    L = info_list[0].split("|")
                    if len(L) == 5:
                        item["model"] = L[1].strip()
                        item["area"] = L[2].strip()
                        item["direction"] = L[3].strip()
                        item["perfect"] = L[4].strip()
                    else:
                        item["model"] = item["area"] = item["direction"] = item["perfect"] = None
                else:
                    item["model"] = item["area"] = item["direction"] = item["perfect"] = None
    
                # 楼层
                item["floor"] = (h.xpath('.//div[@class="positionInfo"]/text()')[0] if h.xpath('.//div[@class="positionInfo"]/text()') else None)
                # 区域
                item["address"] = (h.xpath('.//div[@class="positionInfo"]/a/text()')[0] if h.xpath('.//div[@class="positionInfo"]/a/text()') else None)
                # 总价
                item["total"] = (h.xpath('.//div[@class="totalPrice"]/span/text()')[0]+"万" if h.xpath('.//div[@class="totalPrice"]/span/text()') else None)
                # 单价
                item["unit"] = (h.xpath('.//div[@class="unitPrice"]/span/text()')[0] if h.xpath('.//div[@class="unitPrice"]/span/text()') else None)
    
                print(item)
    
        # 保存
        def save_html(self):
            pass
    
        def run(self):
            for i in range(1,20):
                url=self.url.format(i)
                self.parse_html(url)
                # time.sleep(random.randint(1,3))
                #没抓取一页要初始化self.blag
                self.blag=1
    
    
    if __name__ == '__main__':
        l = LianJiaSpider()
        l.run()
    

    任务1 - 猫眼电影数据抓取

    实现分析

    1、基准xpath: 匹配所有电影信息的节点对象列表
        
        
    2、遍历对象列表,依次获取每个电影信息
       for dd in dd_list:
    	   电影名称 :
    	   电影主演 :
    	   上映时间 :
    

    代码实现

    import requests
    from lxml import etree
    from day01.User_Agent import getheaders
    
    class MaoYanSpisder:
        def __init__(self):
            self.url="https://maoyan.com/board/4"
            self.headers=getheaders()
        def save_hrml(self):
            html=requests.get(url=self.url, headers=self.headers).text
            #解析
            parse_html=etree.HTML(html)
            dd_list=parse_html.xpath("//dd")
            item={}
            for dd in dd_list:
                item["name"]=dd.xpath('.//p[@class="name"]/a/text()')[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):
            self.save_hrml()
    
    if __name__ == '__main__':
        m=MaoYanSpisder()
        m.run()
    

    任务2 - 百度贴吧图片抓取

    目标思路

    • 目标
    抓取指定贴吧所有图片
    
    • 思路
    1、获取贴吧主页URL,下一页,找到不同页的URL规律
    2、获取1页中所有帖子URL地址: [帖子链接1,帖子链接2,...]
    3、对每个帖子链接发请求,获取图片URL
    4、向图片的URL发请求,以wb方式写入本地文件
    

    实现步骤

    • 贴吧URL规律
    http://tieba.baidu.com/f?kw=??&pn=50
    
    • xpath表达式
    1、帖子链接xpath
       //div[@class="t_con cleafix"]/div/div/div/a/@href
        
    2、图片链接xpath
       //div[@class="d_post_content j_d_post_content  clearfix"]/img[@class="BDE_Image"]/@src
        
    3、视频链接xpath
       //div[@class="video_src_wrapper"]/embed/@data-video
       # 注意: 此处视频链接前端对响应内容做了处理,需要查看网页源代码来查看,复制HTML代码在线格式化
    

    任务3 - 电影天堂(xpath)

    你可能感兴趣的:(爬虫,数据解析)