美文网首页
怎样使爬虫更简单

怎样使爬虫更简单

作者: 凌屿 | 来源:发表于2018-12-30 19:04 被阅读9次

    爬虫呢,在我的文章中有说过,但是在方法上,还可以做到个更加简洁,
    顺带总结一下自己的知识。

    requests库

    当请求一个url地址的时候,使用request的话需要使用urllib下的urlopen方法,在requests下,则可以直接根据发起请求的方式来使用方法,get或者post。
    最基本的GET:

    response = requests.get("http://www.baidu.com/")
    

    response的常用方法:

    • response.text 返回解码后的字符串
    • respones.content 以字节形式(二进制)返回。
    • response.status_code  响应状态码
    • response.request.headers  请求的请求头
    • response.headers  响应头
    • response.encoding = 'utf-8' 可以设置编码类型
    • response.encoding 获取当前的编码
    • response.json() 内置的JSON解码器,以json形式返回,前提返回的内容确保是json格式的,不然解析出错会抛异常

    最基本的POST:

    response = requests.post(url=url, data = data)
    

    对于 POST 请求来说,我们一般需要为它增加一些参数。那么最基本的传参方法可以利用 data 这个参数

    在写爬虫的时候,最重要的就是获取页面源码并解析自己想要的数据,那么,使用让自己方便的工具则非常重要,下面说几个常用的python库

    • 一般来讲对我们而言,需要抓取的是某个网站或者某个应用的内容,提取有用的价值。内容一般分为两部分,非结构化的数据 和 结构化的数据。
    • 非结构化数据:先有数据,再有结构
    • 结构化数据:先有结构、再有数据不同类型的数据,我们需要采用不同的方式来处理。

    非结构化的数据处理:

    • 文本、电话号码、邮箱地址
      正则表达式
    • HTML 文件
      正则表达式
      XPath
      CSS选择器

    结构化的数据处理

    • JSON 文件
      JSON Path
      转化成Python类型进行操作(json类)
    • XML 文件
      转化成Python类型(xmltodict)
      XPath
      CSS选择器
      正则表达式

    接下来,先说正则表达式()


    上图呢是正则表达式的匹配规则。

    • 正则表达式,又称正规表示式、正规表示法、正规表达式、规则表达式、常规表示法(英语:Regular Expression,在代码中常简写为regex、regexp或RE),是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些匹配某个模式的文本。

    在python中使用正则表达式需要导入re模块

    import re     # 导入模块
    pattern = re.compile('')    # 把正则表达式构建为一个pattern对象
    # 常用的方法有
    # # 从字符串起始位置开始匹配,开头必须符合正则规则,符合返回匹配结果,如果不符合,返回None,单次匹配
    re.match()
    # 在整个字符串中进行匹配,同样单次匹配,匹配不到,返回None
    re.search()
    # 在整个字符串中匹所有符合正则规则的结果,列表形式返回,无结果返回空列表
    re.findall()
    # 在整个字符串中匹所有符合正则规则的结果,返回一个迭代器
    re.finditer()
    # sub 方法用于替换
    re.sub()
    # 根据匹配进行切割字符串,并返回一个列表
    re.split()
    

    当然,正则表达式是强大的,但是比较麻烦,匹配的规则挺多的,所以,下来说的是Xpath语言.
    什么是XPath?

    • XPath (XML Path Language) 是一门在 XML 文档中查找信息的语言,可用来在 XML 文档中对元素和属性进行遍历。

    我们可以先将 HTML文件 转换成 XML文档,然后用 XPath 查找 HTML 节点或元素。

    什么是XML?

    • XML 指可扩展标记语言(EXtensible Markup Language)
    • XML 是一种标记语言,很类似 HTML
    • XML 的设计宗旨是传输数据,而非显示数据
    • XML 的标签需要我们自行定义。
    • XML 被设计为具有自我描述性。
    • XML 是 W3C 的推荐标准

    XML 和 HTML 的区别

    数据格式 描述 设计目标
    XML Extensible Markup Language (可扩展标记语言) 被设计为传输和存储数据,其焦点是数据的内容。
    HTML HyperText Markup Language (超文本标记语言) 显示数据以及如何更好显示数据。
    HTML DOM Document Object Model for HTML (文档对象模型) 通过 HTML DOM,可以访问所有的 HTML 元素,连同它们所包含的文本和属性。可以对其中的内容进行修改和删除,同时也可以创建新的元素。

    选取节点,最常用的路径表达式:

    表达式 描述
    nodename 选取此节点的所有子节点。
    / 从根节点选取。
    // 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
    . 选取当前节点。
    .. 选取当前节点的父节点。
    @ 选取属性。
    导入lxml
    
    from lxml.html import etree
    

    和 lxml 一样,Beautiful Soup 也是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数据。

    • 三种方法比较
    抓取工具 速度 使用难度 安装难度
    正则 最快 困难 无(内置)
    BeautifulSoup 最简单 简单
    lxml 简单 一般
    导入BeautifulSoup
    from bs4 import BeautifulSoup
    

    在说一下pyquery模块

    • pyquery相当于jQuery的python实现,可以用于解析HTML网页等。它的语法与jQuery几乎完全相同,对于使用过jQuery的人来说很熟悉,也很好上手。

    1、.html()和.text() 获取相应的 HTML 块或者文本内容,

    p=pq("<head><title>Hello World!</title></head>")
    
    # 获取相应的 HTML 块
    print (p('head').html())
    
    # 获取相应的文本内容
    print (p('head').text())
    
    输出:
    '''
    <title>hello Word</title>
    Hello World!
    '''
    

    2、(selector):通过选择器来获取目标内容,

    d = pq(
    "<div><p id='item-0'>test 1</p><p class='item-1'>test 2</p></div>"
    )
    
    # 获取 <div> 元素内的 HTML 块
    print (d('div').html())
    
    # 获取 id 为 item-0 的元素内的文本内容
    print (d('#item-0').text())
    
    # 获取 class 为 item-1 的元素的文本内容
    print (d('.item-1').text())
    
    '''输出:
    <p id="item-0">test 1</p><p class="item-1">test 2</p>
    test 1
    test 2
    '''
    

    3、.eq(index):根据索引号获取指定元素(index 从 0 开始)

    d = pq(
    "<div><p id='item-0'>test 1</p><p class='item-1'>test 2</p></div>"
    )
    
    # 获取第二个 p 元素的文本内容
    print (d('p').eq(1).text())
    
    '''输出
    test 2
    '''
    

    4、.find():查找嵌套元素,

    d = pq("<div><p id='item-0'>test 1</p><p class='item-1'>test 2</p></div>")
    
    # 查找 <div> 内的 p 元素
    print d('div').find('p')
    
    # 查找 <div> 内的 p 元素,输出第一个 p 元素
    print d('div').find('p').eq(0)
    
    '''输出:
    <p id="item-0">test 1</p><p class="item-1">test 2</p>
    <p id="item-0">test 1</p>
    '''
    

    5、.filter():根据 class、id 筛选指定元素,

    d = pq("<div><p id='item-0'>test 1</p><p class='item-1'>test 2</p></div>")
    
    # 查找 class 为 item-1 的 p 元素
    print d('p').filter('.item-1')
    
    # 查找 id 为 item-0 的 p 元素
    print d('p').filter('#item-0')
    
    '''输出:
    <p class="item-1">test 2</p>
    <p id="item-0">test 1</p>
    '''
    

    6、.attr():获取、修改属性值,

    d = pq("<div><p id='item-0'>test 1</p><a class='item-1'>test 2</p></div>")
    
    # 获取 <p> 标签的属性 id
    print(d('p').attr('id'))
    
    # 修改 <a> 标签的 class 属性为 new
    print(d('a').attr('class','new'))
    
    '''输出:
    item-0
    <a class="new">test 2</a>
    '''
    

    7、其他操作:

    #添加 class
    .addClass(value):
    #判断是否包含指定的 class,返回 True 或 False
    .hasClass(value):
    #获取子元素
    .children():
    #获取父元素
    .parents():
    #获取下一个元素
    .next():
    #获取后面全部元素块
    .nextAll():
    #获取所有不匹配该选择器的元素
    .not_(selector):
    

    因为一些网页是动态加载的,所以用到了Selenium

    • 是一个Web的自动化测试工具,最初是为网站自动化测试而开发的,类型像我们玩游戏用的按键精灵,可以按指定的命令自动操作,不同是Selenium 可以直接运行在浏览器上,它支持所有主流的浏览器(包括PhantomJS这些无界面的浏览器)。 Selenium 可以根据我们的指令,让浏览器自动加载页面,获取需要的数据,甚至页面截屏,或者判断网站上某些动作是否发生。

    主要使用的方法和代码如下:

    from selenium import webdriver
    import time
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.common.exceptions import NoSuchElementException,TimeoutException
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    # 要想调用键盘按键操作需要引入keys包
    from selenium.webdriver.common.keys import Keys
    
    # 加载网页(使用火狐浏览器加载)
    # driver = webdriver.Firefox(executable_path='/home/ljh/桌面/driver/geckodriver')
    # driver.get('https://www.baidu.com/')
    
    # 加载网页(使用无头浏览器加载)
    # driver = webdriver.PhantomJS(executable_path='/home/ljh/桌面/driver/phantomjs')
    # driver.get('https://www.baidu.com/')
    
    # 加载网页(使用谷歌浏览器加载)
    # 创建chrome参数对象
    # opt = webdriver.ChromeOptions()
    # 把chrome设置成为无界面模式
    # opt.set_headless()
    # driver = webdriver.Chrome(
    options=opt, executable_path='/Users/ljh/Desktop/chromedriver'
    )
    driver = webdriver.Chrome(executable_path='/home/ljh/桌面/driver/chromedriver')
    #设置页面的加载超时时间
    driver.set_page_load_timeout(0.1)
    
    #处理异常
    try:
    driver.get('https://www.baidu.com/')
    driver.save_screenshot('prcture.png')
    except TimeoutException as err:
    print(err)
    
    #定位和操作
    # driver.find_element_by_xpath():根据xpath定位节点
    # driver.find_element_by_class_name():根据类名定位节点
    # driver.find_element_by_partial_link_text():根据部分文本内容定位节点
    # driver.find_element_by_css_selector():根据css选择器定位节点
    # driver.find_element_by_link_text():根据完整文本定位节点
    
    driver.find_element_by_id('kw').send_keys('隔壁老赵')
    driver.find_element_by_id('su').click()
    
    #获取信息
    print(len(driver.page_source))
    print(driver.get_cookies())
    print(driver.current_url)
    print(driver.name)
    
    #清空输入框内容
    driver.find_element_by_id('kw').clear()
    #输入框重新输入内容
    driver.find_element_by_id('kw').send_keys('风景')
    #模拟回车键
    driver.find_element_by_id('su').send_keys(Keys.RETURN)
    #获取当前的url
    
    #截取网页页面(生成当前的页面快照并保存)
    driver.save_screenshot('baidu.png')
    
    #前进和后退
    time.sleep(2)
    driver.back() #后退
    time.sleep(2)
    driver.forward() #前进
    
    #获取属性和获取文本
    time.sleep(2)
    href = driver.find_element_by_xpath('//h3[@class="t"]/a').get_attribute('href')
    title = driver.find_element_by_xpath('//h3[@class="t"]/a').text
    print(href)
    print(title)
    
    #关于cookie相关操作
    cookies_dict = {cookie['name']:cookie['value'] for cookie in driver.get_cookies()}
    print(cookies_dict)
    print(driver.get_cookie('BIDUPSID'))
    # driver.delete_all_cookies()
    # driver.delete_cookie()
    # driver.add_cookie()
    
    
    ###页面等待
    # 因为selenium加载的页面和浏览器一样会渲染页面,并且有些网页加载需要消耗时间
    # ,这时在页面加载出来之前去寻找节点的话,会报异常,所有我们需要添加等待,有时甚至
    # 需要前置等待
    #
    # 强制等待
    # time.sleep(5)
    #
    # 隐士等待(设置等待时间)
    # driver.implicitly_wait(10)
    
    # 是指显示等待:设置最大的等待时间
    # 直到某一条件成立然后继续执行
    # WebDriverWait(driver,10).until(EC.presence_of_element_located(By.ID,''))
    
    #退出
    driver.close() #退出当前页面
    driver.quit() #退出浏览器
    

    页面的相关操作:

    • Selenium 的 WebDriver提供了各种方法来寻找元素,假设下面有一个表单输入框如下:
    <input type="text" name="user-name" id="passwd-id">
    

    获取id标签值

    element = driver.find_element_by_id("passwd-id")
    

    获取name标签值

    element = driver.find_element_by_name("user-name")
    

    获取标签名值

    element = driver.find_elements_by_tag_name("input")
    

    简单的方法有了以后,当然会追求效率了,所起常用多线程或者多进程来进行爬虫

    • 线程,使用threading模块
    • 线程-注意点:
      线程执行代码的封装 通过上一小节,能够看出,通过使用threading模块能完成多任务的程序开发,为了让每个线程的封装性更完美,所以使用threading模块时,往往会定义一个新的子类class,只要继承threading.Thread就可以了,然后重写run方法

    总结

    • 每个线程默认有一个名字,尽管上面的例子中没有指定线程对象的name,但是python会自动为线程指定一个名字。
    • 当线程的run()方法结束时该线程完成。
    • 无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。

    多线程-共享全局变量
    总结:

    • 在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据
    • 缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)

    互斥锁(重点)

    当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制

    多进程

    进程:一个程序运行起来后,代码+用到的资源 称之为进程,它是操作系统分配资源的基本单元。
    不仅可以通过线程完成多任务,进程也是可以的

    1. 进程的状态
      工作中,任务数往往大于cpu的核数,即一定有一些任务正在执行,而另外一些任务在等待cpu进行执行,因此导致了有了不同的状态


    • 就绪态:运行的条件都已经慢去,正在等在cpu执行
    • 执行态:cpu正在执行其功能
    • 等待态:等待某些条件满足,例如一个程序sleep了,此时就处于等待态

    进程的创建-multiprocessing

    • multiprocessing模块就是跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事情

    进程间通信-Queue:
    Process之间有时需要通信,操作系统提供了很多机制来实现进程间的通信。
    Queue的使用 可以使用multiprocessing模块的Queue实现多进程之间的数据传递,Queue本身是一个消息列队程序

    初始化Queue()对象时(例如:q=Queue()),若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到内存的尽头);

    • Queue.qsize():返回当前队列包含的消息数量;
    • Queue.empty():如果队列为空,返回True,反之False ;
    • Queue.full():如果队列满了,返回True,反之False;
    • Queue.get(block, timeout):获取队列中的一条消息,然后将其从列队中移除,block默认值为True;

    进程与线程的对比:

    功能

    • 进程,能够完成多任务,比如 在一台电脑上能够同时运行多个QQ
    • 线程,能够完成多任务,比如 一个QQ中的多个聊天窗口

    定义的不同

    • 进程是系统进行资源分配基本单位.
    • 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.
    • 线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享所在进程所拥有的全部资源

    区别

    • 一个程序至少有一个进程,一个进程至少有一个线程.
    • 线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高。
    • 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
    • 线线程不能够独立执行,必须依存在进程中

    优缺点

    • 线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。

    使用场景:

    • 多进程常用来处理计算密集型任务:
    • 计算密集型任务的特点:是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。计算密集型任务可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。
    • 多线程常用来处理IO密集型任务:
    • IO密集型:涉及到网络、磁盘IO的任务都是IO密集型任务,特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。但是也要切记,在执行多任务时,并不是越多线程越好。

    相关文章

      网友评论

          本文标题:怎样使爬虫更简单

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