文章目录

  • 爬虫过程
  • 一、获取网络数据(requests、selenium)
  • 1.`requests`
  • 2.`selenium`
  • 3.常见反爬
  • 4.找数据接口
  • 二、解析数据(从获取到的网络数据中提取有效数据)
  • 1.正则表达式
  • 2.基于`css`选择器的解析器(`bs4`)
  • 3.基于`xpath`的解析器(`lxml`)
  • 三、保存数据:`csv`、Excel
  • 四、完整策略
  • 爬虫过程

    爬虫:获取网络数据(公开的网络)
    
    网络数据来源:网站对应的网页、手机APP
    

    一、获取网络数据(requests、selenium)

    1.requests

  • 定义

    Python获取网络数据的第三方库(基于http或https协议的网络请求)
    
  • 应用场景

    1)直接请求网页地址
    2)对提供网页数据的数据接口发送请求
    
  • 基本用法

    1)对目标网页直接发送请求: 
    requests.get(网页地址):获取指定页面的数据返回一个响应对象
    2)获取响应的状态码:response.status_code
    3)获取响应头:response.headers
    4)请求内容(返回的有效数据):
    a.response.content: 二进制类型的数据(图片、视频、音频等,例如:图片下载)
    b.response.text: 字符串类型的数据(网页)
    c.response.json(): 对请求内容做完json解析后的数据(json数据接口)
    
    response = requests.get('https://cd.zu.ke.com/zufang')
    print(response)    # 200表示请求成功
    
  • 设置cookie
    自动登录原理:人工在浏览器上完成登录操作,获取登录后的cookie信息(登录信息),再通过代码发送请求的时候携带登录后的cookie。

    import requests
    
    headers = {
        'cookie': '复制到的cookie值',
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
    }
    
    response = requests.get('https://www.zhihu.com/', headers=headers)
    print(response.text)
    
  • 代理IP
    ❀获取代理IP(可能需要花钱买)

    首先需要去代理IP网页获取一个代理IP:极光代理IP- https://www.jghttp.com/
    

    ❀使用代理IP的语法:

    1.创建代理对应的字典
    # 方法1
    proxies = {
       'http': '221.10.105.215:4531',
    	'https': '221.10.105.215:4531'
    }
    
    # 方法2
    proxies = {
      'http': 'http://221.10.105.215:4531',
      'https': 'http://221.10.105.215:4531'
    }
    response = requests.get(需要获取信息的网址, headers=headers, proxies=proxies)
    print(response.text)
    

    完整代码:

    import requests
    
    headers = {
    	 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
    }
    
    # 1.创建代理对应的字典
    # 方法1
    proxies = {
    	 'http': '221.10.105.215:4531',
    	 'https': '221.10.105.215:4531'
    }
    
    # 方法2
    # proxies = {
     #     'http': 'http://221.10.105.215:4531',
    #     'https': 'http://221.10.105.215:4531'
    # }
    response = requests.get('https://movie.douban.com/top250', headers=headers, proxies=proxies)
     print(response.text)
    
  • 2.selenium

  • 安装第三方模块:selenium
    ❀使用pycharm安装,file->settings->project->点击里面模块右边的加号安装
    ❀或者直接在teiminal里输入 pip(3) install requests

  • 下载驱动器

    1)查看浏览器版本:chrome://version/

    2)chromedriver国内镜像:https://registry.npmmirror.com/binary.html?path=chromedriver/0

    3)到与浏览器最接近的文件夹进去下载,最新的是104.0.5112.81

    4)将下载好压缩包解压移动到python环境安装目录里。

    在pycharm里面的settings里面查找环境目录,将解压好的chromedriver.exe放在python.exe同一目录。
    

  • 获取网页数据

    1.创建浏览器对象(浏览器对象如果是全局变量,浏览器不会自动关闭)
    2.打开网页
    3.获取网页源代码
    4.关闭浏览器
    
    # 导入模块
    from selenium.webdriver import Chrome
    
    # 1.创建浏览器对象
    b = Chrome()
    # 2.打开网页
    b.get('https://movie.douban.com/top250')
    # 3.获取网页源代码: b.page_source
    print(b.page_source)
    # 4.关闭浏览器
    b.close()
    
  • 控制浏览器基础操作

    1.输入框输入内容
    第一步:找到输入框
    第二步:输入框输入内容-传值(标签.send_keys)
    2.点击按钮
    第一步:找到需要点击的标签
    第二步:点击(标签.click())
    3.切换选项卡
    1)获取当前浏览器上所有的窗口(选项卡):b.window_handles
    2)切换选项卡:b.switch_to.window(切换到的窗口)
    注意:selenium中,浏览器对象默认指向一开始打开的选项卡。除非用代码切换,否则浏览器对象指向的选项卡不会切换
    
    # 1.输入框输入内容
    # 1)找到输入框
    input_tag = b.find_element_by_id('key')
    # 2)输入框输入内容
    input_tag.send_keys('电脑\n')
    
    # 2.点击按钮
    # 1)找到需要点击的标签
    btn = b.find_element_by_css_selector('#navitems-group2 .b')
    # 2)点击标签
    btn.click()
    
    # 3.切换选项卡
    # 进入中国知网输入‘数据分析’之后利用切换选项卡获取每篇论文的摘要
    
    from selenium.webdriver import Chrome
    from time import sleep
    from bs4 import BeautifulSoup
    
    # 1.基本操作
    b = Chrome()    # 创建浏览器对象
    b.get('https://www.cnki.net/')    # 打开中国知网
    
    # 获取输入框并输入内容
    search_tag = b.find_element_by_id('txt_SearchText')
    search_tag.send_keys('数据分析\n')
    sleep(4)      # 切换界面等待一会
    
    # 获取需要点击的所有标签:如果拿到标签后需要点击或者输入,必须通过浏览器对象获取标签
    results = b.find_elements_by_css_selector('.result-table-list .name>a')
    
    for i in range(len(results)):
        # 点击第一个结果(这会打开新的选项卡)
        results[i].click()
        b.switch_to.window(b.window_handles[-1])   # 切换选项卡到点开的论文窗口
    
        # 解析内容
        soup = BeautifulSoup(b.page_source, 'lxml')
        result = soup.select_one('#ChDivSummary')    # 获取摘要
        # 没有摘要输出无
        if not result:
            print('无')
        else:
            print(result.text)
            
        b.close()    # 关闭当前指向的窗口(最后一个窗口),窗口关闭后,浏览器对象的指向不会改变。
        sleep(2)
        
        # 回到第一个窗口点击下一个搜索结果
        b.switch_to.window(b.window_handles[0])    # 切换到第一个页面
    
    b.close()   # 关闭浏览器
    
  • 页面滚动

    语法:
    window.scrollBy(x方向偏移量,y方向偏移量)
    执行滚动操作 - 执行js中的滚动代码: 
    
    for x in range(10):
    	b.execute_script('window.scrollBy(0,800)')
    	sleep(1)
    

    需要等页面滚动之后把信息加载完才能解析数据,否则数据可能不完整。

  • 设置cookie

    ❀获取cookie

    第一步:打开需要完成自动登录的网站(需要获取cookie的网站)
    第二步:给足够长的时间让人工完成自动登录并且刷新出登录后的页面
    	   注意:一定要将第一个页面刷新出登录后的状态!!!
    第三步:获取登陆后的cookie并将其保存在本地文件中
    	   get_cookies()
    
    from selenium.webdriver import Chrome
    from json import dumps
    
    b = Chrome()
    
    # 1.打开需要完成自动登录的网站(需要获取cookie的网站)
    b.get('https://www.taobao.com/')
    
    # 2.给足够长的时间让人工完成自动登录并且刷新出登录后的页面
    # 一定要将第一个页面刷新出登录后的状态。
    input('已经完成登录:')
    
    # 3.获取登陆后的cookie并将其保存在本地文件中
    cookies = b.get_cookies()
    print(cookies)
    
    with open('files/taobao.txt', 'w', encoding='utf-8') as f:
        f.write(dumps(cookies))    # 将获取的cookie转换成json写入文件。
    print('写入完成!')
    
    b.close()
    

    ❀使用cookie

    第一步:打开需要完成自动登录的网站(需要获取cookie的网站)
    第二步:从文件读出数据添加cookie
    第三步:重新打开需要登录的网页
    
    from selenium.webdriver import Chrome
    from json import loads
    b = Chrome()
    
    # 1.打开需要完成自动登录的网站(需要获取cookie的网站)
    b.get('https://www.taobao.com/')
    
    # 2.从文件读出数据添加cookie
    with open('files/taobao.txt', encoding='utf-8') as f:
        content = f.read()
        cookies = loads(content)   # 将json数据转换成python格式
    
    for x in cookies:
        b.add_cookie(x)
    
    # 3.重新打开需要登录的网页
    b.get('https://www.taobao.com/')
    
    input('结束')
    b.close()
    
  • 代理IP

    ❀导入相关模块

    from selenium.webdriver import Chrome, ChromeOptions
    

    ❀创建配置对象

    options = ChromeOptions
    

    ❀添加配置

    options.add_argument('--proxy-server=http://ip')
    

    ❀通过指定配置创建浏览器对象

    b = Chrome(options=options)
    

    ❀通过浏览器对象获取网页

    b.get('https://movie.douban.com/topp250')
    

    ❀完整代码

    from selenium.webdriver import Chrome, ChromeOptions
    
    # 1.创建配置对象
    options = ChromeOptions
    # 2.添加配置
    options.add_argument('--proxy-server=http://ip')
    # 3.通过指定配置创建浏览器对象
    b = Chrome(options=options)
    
    b.get('https://movie.douban.com/topp250')   # 网址是被封了的
    
  • 基本配置

    ❀配置前需要自己创建配置对象

    from selenium.webdriver import ChromeOptions, Chrome
    options = ChromeOptions()
    

    ❀取消设置环境提示

    # 固定写法,有需要可添加快捷导入
    options.add_experimental_option('excludeSwitches', ['enable-automation'])
    

    ❀设置取消图片加载—加快运行速度

    # 固定写法,需要添加快捷导入
    options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})
    

    ❀通过浏览器对象获取网页

    b = Chrome(options=options)
    b.get('https://www.jd.com')
    

    ❀完整代码

    from selenium.webdriver import ChromeOptions, Chrome
    
    options = ChromeOptions()
    # 1.取消设置环境提示
    options.add_experimental_option('excludeSwitches', ['enable-automation'])
    
    # 2.设置取消图片加载
    options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})
    
    b = Chrome(options=options)
    b.get('https://www.jd.com')
    
  • 等待

    ❀隐式等待: implicitly_wait(时间(秒))

    a.没有设置隐式等待:在通过浏览器获取标签时,如果标签不存在会直接报错;
    b.设置了隐式等待:在通过浏览器获取标签时,如果标签不存在不会直接报错,不会马上报错,而是在指定时间范围内不断尝试重新获取标签,直到获取到标签或者超时为止(超时会报错);
    c.一个浏览器只需设置一次隐式等待时间,它会作用于这个浏览器每次获取标签的时候。
    
    from selenium.webdriver import ChromeOptions, Chrome
    
    options = ChromeOptions()
    # 1.取消设置环境提示
    options.add_experimental_option('excludeSwitches', ['enable-automation'])
    # 2.设置取消图片加载
    options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})
    
    b = Chrome(options=options)
    b.get('https://www.jd.com')
    
    # 设置隐式等待时间,获取标签时才有效
    b.implicitly_wait(5)
    print('------')
    
    input_tag = b.find_element_by_id('key')
    input_tag.send_keys('钱包\n')
    
    b.close()
    

    ❀显式等待:等待某个条件成立或者不成立为止

    1)创建等待对象:WebDriverWait(浏览器对象,超时时间)
    2)添加等待条件:
    等待对象.until(条件)  -  等到条件成立为止
    等待对象.until_not(条件)  -  等到条件不成立为止
    
    条件写法:
    presence_of_element_located(标签)    -   指定标签出现
    text_to_be_present_in_element(标签, 值)   -   指定标签的value属性中包含指定值
    text_to_be_present_in_element_value(标签, 值)  -  指定标签的标签内容中包含指定值
    
    注意:条件中提供标签的方式
    By.xxx(xxx)
    
    from selenium.webdriver.support.ui import  WebDriverWait
    from selenium.webdriver.support import  expected_conditions as EC
    from selenium.webdriver.common.by import By
    
    options = ChromeOptions()
    # 1.取消设置环境提示
    options.add_experimental_option('excludeSwitches', ['enable-automation'])
    # 2.设置取消图片加载
    options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})
    
    b = Chrome(options=options)
    b.get('https://www.jd.com')
    
    wait = WebDriverWait(b, 10)    # 创建
    wait.until(EC.text_to_be_present_in_element_value(By.ID, 'key'), '电脑')
    print(b.page_source)
    
  • 3.常见反爬

  • 浏览器伪装

    import requests
    
    headers = {
            'user-agent': 自己电脑浏览器信息
    }
    
    response = requests.get('https://movie.douban.com/top250', headers=headers)
    
  • 登录反爬

    解决办法:
    设置cookie
    requests和selenium里分别写了
    
  • 代理IP

    requests和selenium里分别写了
    
  • 4.找数据接口

  • 第一步:打开控制台

  • 第二步:

  • 第三步:

  • 二、解析数据(从获取到的网络数据中提取有效数据)

    1.正则表达式

    # 例子
    # 名字里面有换行,用单行匹配(?s)
    names = findall(r'(?s)<a class="twoline".+?>(.+?)</a>', result)
    

    2.基于css选择器的解析器(bs4

  • bs4的作用

    专门用来解析网页数据的第三方库。(基于css选择器解析网页数据)
    这个库下载时用'beautifulsoup4', 使用的时候用'bs4'
    
    注:使用bs4做数据解析时需要依赖'lxml'这个第三方库
    
  • 导入模块

    from bs4 import BeautifulSoup
    
  • bs4的用法

    1)准备需要解析的数据(获取网页数据)
    2)基于网页源代码创建BeautifulSoup对象
    3)获取标签
    	soup.select(css选择器):获取css选择器选中的所有标签,返回值是一个列表,列表中的元素是标签对象。
    	soup.select_one(css选择器):获取css选择器选中的第一个标签,返回值是一个标签对象。
    	标签对象.select(css选择器):在指定标签中获取css选择器选中的所有标签,返回值是一个列表,列表中的元素是标签对象。
    	标签对象.select_one(css选择器):在指定标签中获取css选择器选中的第一个标签,返回值是一个标签对象。
    4)获取标签内容和标签属性
    a.获取标签内容:标签对象.text
    b.获取标签属性:标签对象.attr[属性名]
    
    # 一个bs4使用的完整代码
    
    # 导入解析相关类
    from bs4 import BeautifulSoup
    
    # bs4的用法
    # 1)准备需要解析的数据(获取网页数据)
    html = open('files/test.html', encoding='utf-8').read()
    
    # 2)基于网页源代码创建BeautifulSoup对象:soup对象代表网页对应的html标签(整个网页)
    soup = BeautifulSoup(html, 'lxml')
    
    # 3)获取标签
    # soup.select(css选择器):获取css选择器选中的所有标签,返回值是一个列表,列表中的元素是标签对象。
    # soup.select_one(css选择器):获取css选择器选中的第一个标签,返回值是一个标签对象。
    result = soup.select('p')
    print(result)    # [<p>你是大笨蛋</p>]
    
    result = soup.select_one('p')
    print(result)    # <p>你是大笨蛋</p>
    
    # 标签对象.select(css选择器):在指定标签中获取css选择器选中的所有标签,返回值是一个列表,列表中的元素是标签对象。
    # 标签对象.select_one(css选择器):在指定标签中获取css选择器选中的第一个标签,返回值是一个标签对象。
    
    # 4)获取标签内容和标签属性
    p = soup.select_one('p')
    img = soup.select_one('img')
    # a.获取标签内容:标签对象.text
    print(p.text)    # 你是大笨蛋
    # b.获取标签属性:标签对象.attr[属性名]
    print(img.attrs['src'])    # 123
    
  • 3.基于xpath的解析器(lxml

    html的树结构:

  • xpath基本概念

    ❀树:整个html(xml)代码结构是一个树结构

    ❀节点:树结构中的每一个元素(标签)就是一个节点

    ❀根节点(根元素):html或者xml最外面的那个标签(元素)

    ❀节点内容:标签内容

    ❀节点属性:标签属性

  • xml数据格式

    xml和json一样,是一种通用的数据格式(大部分编程语言都支持的数据格式)

    xml是通过标签(元素)的标签内容和标签属性来保存数据的。

    <supermarket name="永辉超市" address="肖家河大厦">
        <staffs>
            <staff  id="s001" class="c1">
                <name>小明</name>
                <position>收营员</position>
                <salary>4000</salary>
            </staff>
            <staff  id="s002" class="c2">
                <name>小花</name>
                <position>促销员</position>
                <salary>3500</salary>
            </staff>
            <staff  id="s003" class="c1">
                <name>张三</name>
                <position>保洁</position>
                <salary>3000</salary>
            </staff>
            <staff  id="s004" class="c2">
                <name>李四</name>
                <position>收营员</position>
                <salary>4000</salary>
            </staff>
            <staff  id="s005" class="c1">
                <name>王五</name>
                <position>售货员</position>
                <salary>3800</salary>
            </staff>
        </staffs>
    
        <goodsList>
            <goods discount="0.9">
                <name>泡面</name>
                <price>3.5</price>
                <count>120</count>
            </goods>
            <goods class="c1">
                <name>火腿肠</name>
                <price>1.5</price>
                <count>332</count>
            </goods>
            <goods>
                <name>矿泉水</name>
                <price>2</price>
                <count>549</count>
            </goods>
            <goods discount="8.5">
                <name>面包</name>
                <price>5.5</price>
                <count>29</count>
            </goods>
        </goodsList>
    </supermarket>
    
  • 导入lxml模块

    第一步:安装lxml模块 –

    点击pycharm的Terminal输入

    pip install lxml
    

    第二步:在项目里面导入

    from lxml import etree
    
  • xpath语法

    ❀创建树结构获取树的根节点

    html.etree.XML(xml数据)    # 需要解析的是xml数据
    html.etree.HTML(html数据)   # 需要解析的是html数据
    

    ❀根据xpath获取指定标签

    节点对象.xpath(路径):
        返回路径对应的所有的标签,返回值是列表,列表中的元素是标签对象(节点对象)
    

    1.绝对路径:

    /标签在树结构中的全路径 (路径必须从根节点开始写)。

    2.相对路径:

    路径开头用 “.” 标签当前节点(xpath前面是谁,'.'就代表谁),“…” 表示当前节点的上层节点。

    3.全路径:

    用**‘//’**开头的路径 – 在整个树中获取节点。

    获取上面xml数据的一些实例:

    # 绝对路径
    res = root.xpath('/supermarket/staffs/staff/name/text()')
    print(res)    # ['小明', '小花', '张三', '李四', '王五']
    
    # 相对路径
    sta1 = root.xpath('./staffs/staff')[0]  
    # 获取第一个员工对应的staff标签
    res = sta1.xpath('./name/text()')      
    # xpath前面是谁,'.'就代表谁
    print(res)    # ['小明']
    
    # 全路径
    res = root.xpath('//name/text()')
    print(res)    # ['小明', '小花', '张三', '李四', '王五', '泡面', '火腿肠', '矿泉水', '面包']
    

    ❀获取标签内容

    语法:节点对象.xpath(获取标签的路径/text())
    含义:获取指定路径下所有标签的标签内容
    
    res = root.xpath('//name/text()')
    print(res)    # ['小明', '小花', '张三', '李四', '王五', '泡面', '火腿肠', '矿泉水', '面包']
    

    ❀获取标签属性值

    节点对象.xpath(获取标签的路径/@属性名)
    
    result = root.xpath('/supermarket/@name')
    

    ❀谓语(条件)

    1.位置相关谓语

    [N]    -    第N个
    [last()]   -   最后一个
    [last()-N] - 倒数第N+1个:[last()-1] - 倒数第2个
    [position()>N]、[position()<N]、[position()>=N]、[position()<=N]
    
    result = root.xpath('//staff[1]/name/text()')
    print(result)    # ['小明']
    

    2.属性相关谓语

    [@属性名=属性值]:获取指定属性是指定值的标签
    [@属性名]: 拥有指定属性的标签
    
    # 1
    # staff[@class="c1"] == staff.c1
    result = root.xpath('//staff[@class="c1"]/name/text()')
    print(result)    # ['小明', '张三', '王五']
    
    # 2获取有discount这个属性的name标签的内容
    result = root.xpath('//goods[@discount]/name/text()')
    print(result)    # ['泡面', '面包']
    

    3.根据子标签内容相关谓语

    根据子标签的内容来筛选,筛选的是父标签
    
    [子标签名>数据]
    [子标签名<数据]
    [子标签名>=数据]
    [子标签名<=数据]
    [子标签名=数据]
    
    # 获取商品价格大于2的商品name标签内容
    result = root.xpath('//goods[price>=2]/name/text()')
    print(result)    # ['泡面', '矿泉水', '面包']
    

    ❀通配符: *

    路径中*来表示所有标签或所有属性
    
    # 例子
    # 获取第一个员工的所有标签内容
    result = root.xpath('//staff[1]/*/text()')
    print(result)    # ['小明', '收营员', '4000']
    
    # 获取所有属性值为c1的name标签内容
    result = root.xpath('//*[@class="c1"]/name/text()')
    print(result)    # ['小明', '张三', '王五', '火腿肠']
    
    # 获取商品里面有属性的所有name标签内容
    result = root.xpath('//goods[@*]/name/text()')
    print(result)    # ['泡面', '火腿肠', '面包']
    

    ❀若干路径:|

    路径1|路径2|......:同时获取路径1和路径2...的内容
    
    # 同时获取两个路径的内容
    result = root.xpath('//goods/name/text()|//staff/position/text()')
    print(result)    
    # ['收营员', '促销员', '保洁', '收营员', '售货员', '泡面', '火腿肠', '矿泉水', '面包']
    
  • 案例:豆瓣使用多线程解析十页数据

    import requests
    from lxml import etree
    from threading import Thread
    from datetime import datetime
    import csv
    
    f = open('files/电影.csv', 'a', encoding='utf-8', newline='')
    write = csv.writer(f)
    write.writerow(['电影名字', '评分', '评论', '简介'])
    
    
    # 获取网页数据并解析数据
    def get_net_data(url):
        print(f'开始解析: {datetime.now()}')
        # 自己的电脑配置信息
        headers = {
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
        }
        response = requests.get(url=url, headers=headers)
        analysis_data(response.text)
        print(f'解析结束: {datetime.now()}')
    
    
    # 解析数据
    def analysis_data(html:str):
        # xpath解析数据
        root = etree.HTML(html)
    
        all_data = []
        all_film_div = root.xpath('//ol[@class="grid_view"]/li/div')
        for div in all_film_div:
            name = div.xpath('./div[@class="info"]/div[1]/a/span[1]/text()')[0]
            score = div.xpath('./div[@class="info"]/div[2]/div/span[2]/text()')[0]
            comment = div.xpath('./div[@class="info"]/div[2]/div/span[last()]/text()')[0]
            brief = div.xpath('./div[@class="info"]/div[2]/p[@class="quote"]/span/text()')
            if brief:
                brief = brief[0]
            else:
                brief = None
            all_data.append([name, score, comment, brief])
    
        print(all_data)
        write.writerows(all_data)
        print('写入完成!')
    
    
    if __name__ == '__main__':
        for i in range(0, 251, 25):
            url = f'https://movie.douban.com/top250?start={i}&filter='
    
            t = Thread(target=get_net_data, args=(url,))
            t.start()
    

  • 三、保存数据:csv、Excel

    前面有文章讲了数据写入和读出。

    四、完整策略

    物联沃分享整理
    物联沃-IOTWORD物联网 » 爬虫过程和反爬

    发表评论