Skip to content

Web Crawler

Capture 2 爬虫基础

2.1 HTTP基本原理

2.1.1 URI和URL

通过一个链接, 我们便可以从互联网上到这个资源,这就是URL/URI 。 URN只命名资源而不指定如何定位资源,比如um:isbn:0451450523指定了一本书的ISBN,可以唯一标识这本书,但是没有指定到哪里定位这本书,这就是URN。

2.1.2 超文本

网页的源代码HTML 就可以称作超文本。

2.1.3 HTTP和 HTTPS

https:基于ssl安全层的http

2.1.5 请求

  1. 请求方法 :常见的请求方法有两种:GET和POST。 · GET请求中的参数包含在URL里面,数据可以在URL中看到,而POST请求的URL不会包 含这些数据, · GET请求提交的数据最多只有1024字节,而POST方式没有限制。
  2. 请求的网址:URL
  3. 请求头:用来说明服务器要使用的附加信息,比较重要的信息有Cookie、Referer、User-Age等。
  4. 请求体:一般承载的内容是 POST请求中的表单数据,而对于GET请求,请求体则为空。

2.1.6 响应

由服务端返回给客户端,可以分为三部分:响应状态码、响应头和响应体; 1. 响应状态码 响应状态码表示服务器的响应状态:

  1. 响应头:响应头包含了服务器对请求的应答信息:如Content-Type(文档类型)、Server(包含服务器的信息)、Set-Cookie等
  2. 响应体:响应的正文数据都在响应体中,

2.2 网页基础

2.2.1 网页的组成——HTML、CSS和JavaScript。

  1. HTML 超文本标记语言。
  2. CSS 层叠样式表,为了让网页看起来更好看一些,
  3. JavaScripct 实现了一种实时、 动态、交互的页面功能。

2.2.2 网页的结构

A simple Example

<!-- simple_example.html -->
<!DOCTYPE html> 
<html> 
<head> <!-- 网页头(标签页)-->
<meta charset="UTF-8"> 
<title>This is a Demo</title> 
</head> 
<body> <!-- 网页体:网页正文中显示的内容 -->
<div id="container"> <!-- div 标签定义了网页中的区块, 
它的id是 container, id的内容在网页中是唯一的,我们可以通过它来获取这个区块-->
<div class="wrapper"> <!--然后在此区块内又有一个div标签,
它的 class 为 wrapper, 经常与CSS配合使用-->
<h2 class="title">Hello World</h2> <!--h2代表一个二级标题-->
<p class="text">Hello, this is a paragraph.</p> </div> <!--p标签代表一个段落-->
</div> 
</body> 
</html> 

2.2.3 节点树及节点间的关系

在HTML中, 所有标签定义的内容都是节点, 它们构成了1个HTML DOM树。

DOM:文档对象模型,是W3C(万维网联盟)的标准。W3C DOM标准被分为3个不同的部分: * 核心 DOM:针对任何结构化文档的标准模型。 * XML DOM:针对XML文档的标准模型。 * HTML DOM:针对HTML文档的标准模型。

2.2.4 选择器

在CSS中,我们使用 CSS选择器来定位节点。 上例中div节点的id为container,那么就可以表示为#container,#开头代表选择id, 其后紧跟id的名称. 另外, 如果我们想选择class为wrapper的节点,便可以使用.wrapper,这里以点(.)开头代表选择class,其后紧跟class的名称。 另外,还有一种选择方式,那就是根据标签名筛选,例如想选择二级标题,直接用h2即可。 这是最常用的3种表示,分别是根据id、class、标签名筛选.

2.3 爬虫的基本原理

2.3.1 爬虫概述

  1. 获取网页(源代码):最关键的部分就是构造一个请求并发送给服务器, 然后接收到响应并将其解析出来
  2. 提取信息:获取网页源代码后,接下来就是分析网页源代码;
  3. 保存数据
  4. 自动化程序

2.3.2 能抓怎样的数据

常规网页、JSON字符串、二进制数据, 如图片、 视频和音频等。

2.3.3 JavaScript渲染页面

得到的源代码实际和浏览器中看到的不一样,原始的 HTML 代码就是空壳,Be like

<!DOCTYPE html> 
<html> 
<head> 
<meta charset="UTF-8"> 
<title>This is a Demo</title> </head> 
<body> 
<div id="container"> 
</div> 
</body> 
<script src="app.js"></script> </html> 
body 节点里面只有一个过为 container 的节点,但在 body 节点后引入了 app.js负责整个网站的渲染。

2.4 会话和Cookies

2.4.1 静态网页和动态网页

文字、 图片等内容均通过写好的HTML代码来指定的页面叫作静态网页,可维护性差。

2.4.2 无状态HTTP

HTTP的无状态是指HTTP协议对事务处理是没有记忆能力的,意味着如果后续需要处理前面的信息,则必须重传。 因此需要会话和cookies 会话在服务器,来保存用户的会话信息,Cookies在客户端,浏览器在下次访间网页时会自动附带上 它发送给服务器,服务器通过识别Cookies并鉴定出是哪个用户,然后再判断用户是否是登录状态,然后返回对应的响应。

  1. 会话:在Web中,会话对象用来存储特定用户会话所需的属性及配置信息。
  2. Cookies:某些网站为了辨别用户身份、进行会话跟踪而存储在用户本地终端上的数据。
  3. 会话维持:当客户端第一次请求服务器时, 服务器会返回一个请求头带有Set-Cookie字段的响应给客户端,Cookies携带了会话ID信息,服务器检查该Cookies即可找到对应的会话是什么,所以,Cookies和会话需要客户端和服务器端的配合。
  4. 属性结构:Cookies有很多条目, 其中每个条目可以称为Cookie.

  5. 会话cookies和持久cookies :放在浏览器内存里,浏览器关闭即消失/保存到客户端的硬盘中 严格来说,只是由Cookie的Max Age或Expires字段决定了过期的时间。

2.5 代理的基本原理

借助某种方式来伪装我们的IP,让服务器识别不出是由我们本机发起的请求

2.5.1 基本原理

代理实际上指的就是代理服务器,代理网络用户去取得网络信息。

2.5.2 代理的作用

  1. 访问一些平时不能访问的站点。
  2. 访问一些单位或团体内部资源
  3. 提高访问速度
  4. 隐藏真实IP;

Capture 3 基本库的使用

3.1 使用urllib

Python 内置的 HTTP请求库 * request: 它是最基本的 HTTP 请求模块, 可以用来模拟发送请求。 * error: 异常处理模块 * parse:工具模块,提供url处理方法 * robotparser: 主要是用来识别网站的robots.txt文件

3.1.1 发送请求

  1. urlopen() :urllib.request模块
    import urllib.request
    
    response = urllib.request.urlopen("https://www.python.org")
    print(response.status) #状态码
    print(response.getheaders()) #响应的头信息
    print(response.getheader('Connection')) #
    
    urlopen API urllib.request.urlopen(url, data=None, timeout=, cafile=None, capath=None, cadefault=False, context=None)
  2. data 如果要添加该参数, 并且如果它是字节流编码格式的内容, 即 bytes 类型, 则需要通过 bytes()方法转化。 另外,如果传递了这个参数, 则它的请求方式就是POST方式。
    import urllib.parse
    import urllib.request
    
    data = bytes(urllib.parse.urlencode({'word':'hello'}),encoding='utf-8')
    response = urllib.request.urlopen('https://httpbin.org/post',data=data)
    print(response.read())
    

我们传递的参数出现在了form字段中,这表明是模拟了表单提交的方式,以 POST方式传输数据。

  • timeout参数 timeout参数用于设置超时时间,单位为秒,如果不指定该参数, 就会使用全局默认时间。

    import urllib.request
    import socket
    import urllib.error
    try:
        response = urllib.request.urlopen('https://httpbin.org/get',timeout=0.1)
    except urllib.error.URLError as e:
        if isinstance(e.reason ,socket.timeout):
            print("Time Out")
    

  • Request 用法:

    import urllib.request
    
    request = urllib.request.Request('http://python.org')
    response = urllib.request.urlopen(request)
    print(response.read().decode('utf-8'))
    
    其他Request可选参数:data(bytes字节流类型),headers(添加请求头伪装浏览器),请求方法等
    from urllib import request, parse
    
    url = 'http://httpbin.org/post'
    headers = {
        'User-Agent':'Mozilla/4.0(compatible;MSIE 5.5 ; Windows NT)',
        #模拟一个旧版本的 Internet Explorer(MSIE 5.5)和 Windows NT 操作系统。
        'Host': 'Httpbin.org'# 指定目标网站的主机名
    }
    dict = {
        'name' : 'Germay'
    }
    data = bytes(parse.urlencode(dict),encoding='utf-8')
    #将字典转换成 URL 编码格式的字符串,编码后的字符串转换为字节对象
    req = request.Request(url=url,data=data,headers=headers,method='POST')
    response = request.urlopen(req)
    print(response.read().decode('utf-8'))
    
    也可以用add_header写
    req = request.Request(url=url,data=data,method='POST')
    req.add_header('User_Agent','Mozilla/4.0 (compatible;MSIE 5.5;Windows NT)')
    

  • 高级用法 更强大的工具Handler,简而言之就是各种处理器。

    • 验证:有些网站在打开时就会弹出提示框, 直接提示你输入用户名和密码, 验证成功后才能查看页面, 借助HTTPBasicAuthHandler就可以完成
    • Cookies:
      import http.cookiejar , urllib.request
      filename = 'cookie.txt'
      cookie = http.cookiejar.MozillaCookieJar(filename)
      handler = urllib.request.HTTPCookieProcessor(cookie)
      opener = urllib.request.build_opener(handler)
      response = opener.open('http://www.baidu.com')
      cookie.save(ignore_discard=True , ignore_expires=True)
       ```
      搞到了cookie之后
       ```py
      import http.cookiejar , urllib.request
      
      cookie = http.cookiejar.LWPCookieJar()
      cookie.load('cookie.txt',ignore_discard=True,ignore_expires=True)
      handler = urllib.request.HTTPCookieProcessor(cookie)
      opener = urllib.request.build_opener(handler)
      responde = urllib.request.urlopen('http://www.baidu.com')
      print(responde.read().decode('utf-8'))
       ```
      
      
      #### 3.1.2 处理异常
      
      1. URLError
      ```py
      from urllib import request,error
      try:
          response = request.urlopen('http://www.baidu.com/111')
      except error.URLError as e: 
          print(e.reason) 
      
  • HTTPError URLError 的子类,专门用来处理 HTTP 请求错误,有3个属性 1.code: 返回 HTTP 状态码, 2.reason: 3.headers:返回请求头

    from urllib import request,error
    try:
        response = request.urlopen('https://baidu.com')
    except error.HTTPError as e: 
        print(e.reason,e.code,e.headers,sep = '\n') 
    except error.URLError as e:
        print(e.reason)
    else:
        print('Request Successfully')#最后, 用 else 来处理正常的逻辑
    
    比较好的处理异常写法
    有时候的错误为字符串,比如urlopen设置了timeout

3.1.3 解析链接

  1. urlparse() 实现 URL 的识别和分段
    from urllib.parse import urlparse 
    result = urlparse('http://www.baidu.com/index.html;user?id=5#comment') 
    print(type(result), result) 
    
    所以, 可以得出一个标准的链接格式,具体如下: scheme://netloc/path;params?query#fragment
  2. scheme :协议
  3. netloc:网络位置,包括域名、IP地址或端口
  4. path:服务器上的具体资源位置,通常是文件路径。
  5. params:用于传递给资源的可选参数
  6. query:包含查询参数,通常以键值对的形式表示,用 ? 开始,键值对之间以 & 分隔。
  7. fragm:指向资源的片段或内部部分

  8. urlunparse() 构造url

    from urllib.parse import urlunparse
    data = {'http','www.baidu.com','index.html','user','a=6','commet'}
    print(urlunparse(data))
    

  9. urlencode() :构造get请求

    from urllib.parse import urlencode
    
    params = {
        'name' : 'germay',
        'age' : 22
    }
    base_url = 'http://www.baidu.com?'
    url = base_url+urlencode(params)
    print(url)
    

3.1.4分析Robots协议

  1. robotparser
    import urllib.robotparser
    rp = urllib.robotparser.RobotFileParser('http://www.jianshu.com/robots.txt')
    print(rp.can_fetch('*', 'http://www.jianshu.com/p/b67554025d7d'))
    

3.2 使用requests

3.2.1 基本用法

  1. import requests
    
    r = requests.get('http://www.baidu.com')
    print(type(r))
    print(r.status_code)
    print(type(r.text))
    print(r.text)
    print(r.cookies)
    print(r.headers)
    r = requests. post('http://httpbin.org/post') 
    r = requests. put('http: I /httpbin.org/put') 
    r = requests.delete('http://httpbin.org/delete') 
    r = requests. head('http: I /httpbin.org/get') 
    r = requests. options('http://httpbin.org/get') 
    
  2. Get请求

    import requests
    
    data = {
        'name': 'Germay',
        'age' : 22
    }
    r = requests.get('http://httpbin.org/get',params=data)#params附加额外信息
    print(r.text)
    
    请求的链接自动被构建成了"url": "http://httpbin.org/get?name=Germay&age=22"

抓取页面

import requests
import re

headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'
}
r = requests.get('http://www.zhihu.com/explore',headers=headers)
pattern = re.compile('explore-feed.*?question_link.*?>(.*?)</a>',re.S)
titles = re.findall(pattern,r.text)
print(titles)
抓取二进制
import requests
headers = {
     'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36'
}
r = requests.get('http://github.com/favicon.ico',headers=headers)
with open('github.ico','wb') as file:
    file.write(r.content)

  1. Post 请求
    import requests
    
    data = {
        'name':'Germay',
        'age':'22'
    }
    rp = requests.post('http://httpbin.org/post',data=data)
    print(rp.text)
    

3.2.2 高级用法

  1. 文件上传

    import requests
    
    files = {'file':open('github.ico','rb')}
    
    r = requests.post('http://httpbin.org/post',files=files)
    print(r.text)
    

  2. cookies

import requests
r = requests.get('http://www.baidu.com')
print(r.cookies)
for key,value in r.cookies.items():
    print(key + '=' + value)
3. 会话维持
import requests
s = requests. Session() 
s.get('http://httpbin.org/cookies/set/number/123456789')
r = s.get('http://httpbin.org/cookies')
print(r.text)

  1. SSL证书验证
  2. 代理设置
  3. 超时设置
  4. 身份认证
    import requests
    
    r = requests.get('http://localhosu:5000',auth=('username','password'))
    print(r.status_code)
    

3.3 正则表达式

  1. match
    import re
    
    content = 'Hello 123 4567 World_This is a Regex Demo'
    print(len((content)))
    result = re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}',content)
    print(result)
    print(result.group())
    print(result.span())
    
    >>> 41
    >>> <re.Match object; span=(0, 25), match='Hello 123 4567 World_This'>
    >>> Hello 123 4567 World_This
    >>> (0, 25)
    
    import re
    
    content = 'Hello 123 4567 World_This is a Regex Demo'
    print(len((content)))
    result = re.match('^Hello\s(\d+\s\d{4})\s\w{10}',content)
    print(result)
    print(result.group(1))
    
    可以这样单独提取
  2. 通用匹配 .可以匹配任意字符(除换行符) ,*代表匹配前面的字符无限次
    result = re.match('^Hello.*Demo',content)
    
  3. 贪婪/非贪婪 贪婪
    content = 'Hello 1234567 World_This is a Regex Demo'
    result = re.match('^H.*(\d+).*Demo',content)
    print(result.group(1))# only get 7
    
    非贪婪
    content = 'Hello 1234567 World_This is a Regex Demo'
    result = re.match('^H.*?(\d+).*Demo',content)
    print(result.group(1))# get 1234567
    
  4. 修饰符 re.S 使.能够识别换行符,re.I使match对大小写不敏感
  5. 转义匹配

    import re
    content = '(百度)www.baidu.com'
    result = re.match('\(百度\)www\.baidu\.com',content)
    print(result)
    

  6. search 因为 match方法在使用时需要考虑到开头的内容,这在做匹配时并不方便

    import re
    
    html ='''<div id="songs-list")
    <h2 class="title">经典老歌</h2)
    <p class="introduction">
    经典老歌列表
    </p>
    <ul id="list" class="list-group">
    <li data-view="2">一路上有你</li>
    <li data-view="7">
    <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
    </li>
    <li data-view="4" class="active">
    <a href="/3.mp3" singer="齐秦">往事随风</a>
    </li>
    <li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
    <li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
    <li data-view="5">
    <a href="/6.mp3"singer="邓丽君">但愿人长久</a>
    </li>
    </ul>
    </div>
    '''
    
    result = re.search('<li.*?active.*?singer="(.*?)">(.*?)</a>',html,re.S)
    print(result.group(1),result.group(2))
    

  7. findall

results = re.findall('<a.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>',html,re.S)
print(results)
print(type(results))
for result in results:
    print(result)
    print(result[0],result[1],result[2])
  1. sub 用sub去掉原字符串一些东西

    import re
    
    content = '54aKS4yrSoiRS4ixSL2g'
    content = re.sub('\d+','',content)
    print(content)
    
    第一个''表示去掉什么,第二个''表示替换成什么

  2. compile

    import re 
    content1 = '2016-12-15 12:00' 
    content2 = '2016-12-17 12:55' 
    content3 = '2016-12-22 13:21'
    pattern = re.compile('\d{2}:\d{2}') 
    result1 = re.sub(pattern,'', content1) 
    result2 = re.sub(pattern,'', content2) 
    result3 = re.sub(pattern,'', content3) 
    print(result1, result2, result3) 
    

Capture 4 解析库的使用

4.1 使用XPath

  1. XPath 概览 XML Path Language,在XML文档中查找信息的语言

  2. 自动修正

    from lxml import etree 
    text = '''
    <div> 
    <ul> 
    <li class="item-1"><a href="link2.html">second item</a></li> 
    <li class="item-inactive"><a href="link3.html">third item</a></l1> 
    <li class="item-1"><a href="link4.html">fourth item</a></li> 
    <li class="item-0"><a href="link5.html">fifth item</a> 
    </ul> 
    </div> 
    '''
    html = etree.HTML(text) 
    result = etree.tostring(html) # 调用 tostring方法即可输出修正后的 HTML 代码
    # 比如补全li节点标签
    print(result.decode('utf-8')) #但是是字节形式,要转成utf-8
    

  3. 所有节点

    from lxml import etree 
    html = etree.parse('./test.html',etree.HTMLParser())
    result = html.xpath('//*')
    print(result)
    print(type(result))
    # 这里使用*代表匹配所有节点,要《li》就改成//li
    
    result = html.xpath('//li')
    print(result[0])#索引
    

  4. 子节点

    result = html.xpath('//li/a')#li的所有a直接子节点
    result = html.xpath('//li//a')#li的所有a子孙节点
    

  5. 父节点

    from lxml import etree
    html = etree.parse('./test.html',etree.HTMLParser())
    result = html.xpath('//a[@href="link4.html"]/../@class')# '//a[@href="link4.html"]/parent::*/@class'
    print(result)
    

  6. 属性匹配 选取class为item-1的li节点,

    from lxml import etree
    html = etree.parse('./test.html',etree.HTMLParser())
    result = html.xpath('//li[@class="item-0"]')
    print(result)
    
    能知道匹配了2个

  7. 文本获取 text ()可以获取节点内部文本,来尝试获取前面li节点中的文本
    from lxml import etree
    html = etree.parse('./test.html',etree.HTMLParser())
    result = html.xpath('//li[@class="item-0"]/a/text()')## //text :可能会夹杂一些换行符之类的
    print(result)
    
  8. 属性获取 所有li 节点下所有a 节点的href 属性

    from lxml import etree
    html = etree.parse('./test.html',etree.HTMLParser())
    result = html.xpath('//li/a/@href')
    print(result)
    
    属性匹配是中括号加属性名和值来限定某个属性,如[@href="link1.html"], 而此处的@href指的是获取节点的某个属性,二者需要做好区分。

  9. 属性多值匹配 某些节点的某个属性可能有多个值

    from lxml import etree
    text ='''<li class="li li-first"><a href ="link.html">first item</a></li>'''#li 节点的class 属性有两个值li 和li-first
    html = etree.HTML(text)
    result = html.xpath('//li[@class="li li-first"]/a/text()')
    #result = html.xpath('//li[contains(@class,"li")]/a/text()') ,第一个参数传属性名称,第二个参数传属性值
    print(result)
    

  10. 多属性匹配

    from lxml import etree
    text ='''<li class="li li-first" name="item"><a href ="link.html">first item</a></li>'''#li 节点的class 属性有两个值li 和li-first
    html = etree.HTML(text)
    result = html.xpath('//li[contains(@class,"li") and @name ="item"]/a/text()')
    print(result)
    

  11. 按序选择

    from lxml import etree
    
    html = etree.parse('./test.html',etree.HTMLParser())
    result = html.xpath('//li[1]/a/text()')
    result = html.xpath('//li[last()]/a/text()')
    result = html.xpath('//li[position()<3]/a/text()')
    result = html.xpath('//li[last()-2]/a/text()')
    print(result)
    

  12. 节点轴选择

    from lxml import etree
    
    html = etree.parse('./test.html',etree.HTMLParser())
    result = html.xpath('//li[1]/ancestor::*')
    result = html.xpath('//li[1]/ancestor::div')
    result = html.xpath('//li[1]/attribute::*')# attribute轴,可以获取所有属性值
    result = html.xpath('//li[1]/child::a[@href="link1.html"]') #取href属性为link1.html直接子节点的a节点
    result = html.xpath('//li[1]/descendant::*[2]')
    print(result)
    

4.2 Beautiful Soup

  1. 基本用法

    html = '''
    <html><head><title>The Dormouse's story</title></head> 
    <body> 
    <p class="title" name="dromouse"><b>The Dormouse's story</b></p> 
    <p class="story">Once upon a time there were three little sisters; and their names were 
    <a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>, 
    <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and 
    <a href="http://example.com/tillie" class="sister" id="link3" > Tillie</a>; 
    and they lived at the bottom of a well.</p> 
    <p class="story">...<Ip>   
    '''
    from bs4 import BeautifulSoup
    
    soup = BeautifulSoup(html,'lxml')#对于不标准的 HTML 字符串可以自动更正格式
    print(soup.prettify()) #以标准的缩进格式输出
    print(soup.title.string)
    

  2. 节点选择器

    print(soup.title)
    print(type(soup.title))#Tag类型
    print(soup.title.string)
    print(soup.head)
    print(soup.p) #第一个匹配节点
    

  3. 提取信息 (1)获取节点名称:name
    print(soup.title.name) #title
    
    (2)获取属性:attrs
    print(soup.p.attrs)#字典类型 {'class': ['title'], 'name': 'dromouse'}
    print(soup.p.attrs['name']) #相当于从字典取值 print(soup.p['name'])
    
    (3)获取内容:string
    print(soup.p.string)
    
  4. 嵌套选择
    print(soup.head.title)#每一个返回类型都是Tag
    
  5. 关联选择 (1)子节点和子孙节点
    html = '''
    <html> 
    <head> 
    <title>The Dormouse's story</title> 
    </head> 
    <body> 
    <p class="story"> 
    Dnce upon a time there were three little sisters; and their names were 
    <a href="http://example.com/elsie" class="sister" id="link1">
    <span>Elsie</span> 
    </a> 
    <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> 
    and 
    <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a> 
    and they lived at the bottom of a well. 
    </p> 
    <p class="story">... </p> 
    '''
    from bs4 import BeautifulSoup
    
    soup = BeautifulSoup(html,'lxml')
    print(soup.p.contents)
    
    得到一个直接子节点的列表

print(soup.p.children)#是一个生成器,也是遍历直接子节点
for i,child in enumerate(soup.p.children):#enumerate 用于在遍历可迭代对象时,为每个元素提供一个索引值
    print(i,child)
所有子孙节点就把children 改成 descendants

(2)父节点、祖先节点类似,注意查询的是第一个节点的父/祖 父:parent 祖先parents(生成器)

(3)siblings

html = '''
<html> 
<body> 
<p class="story"> 
Once upon a time there were three little sisters; and their names were 
<a href="http://example.com/elsie" class="sister" id="link1"> 
<span>Elsie</span> 
</a> 
Hello 
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> 
and 
<a href="http://example.com/tillie" class="sister" id="link3"> Tillie</a>
and they lived at the bottom of a well.
</p>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html,'lxml')
print('Next sibling',soup.a.next_sibling)
print('Pre sibling',soup.a.previous_sibling)
print('Next Siblings',list(enumerate(soup.a.next_siblings)))#生成器
print('Pre Siblings',list(enumerate(soup.a.previous_siblings)))
(4)提取信息
html = '''
<html> 
<body> 
<p class="story"> 
Once upon a time there were three little sisters; and their names were 
<a href="http://example.com/elsie" class="sister" id="link1">Bob</a><a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> 
</p> 
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html,'lxml')
print(list(soup.a.parents)[0].attrs['class'])
6. 方法选择器 * find_all() (1)name
html = '''
<div class="panel"> 
<div class="panel-heading"> 
<h4>Hello</h4> 
</div> 
<div class="panel-body"> 
<ul class="list" id="list-1"> 
<li class="element">Foo</li> 
<li class="element">Bar</li> 
<li class="element">Jay</li> 
</ul> 
<Ul class="list list-small" id="list-2"> 
<li class="element">Foo</li> 
<li class="element">Bar</li> 
</ul> 
</div> 
</div> 
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html,'lxml')
print(soup.find_all(name='ul'))
print(type(soup.find_all(name='ul')[0]))#Tag

for ul in soup.find_all(name='ul'):
    print(ul.find_all(name= 'li'))
    for li in ul.find_all(name='li'):
        print(li.string)
(2)attrs
print(soup.find_all(attrs={'id':'list-1'}))

(3)text(string)

import re
html = '''
<div class="panel"> 
<div class="panel-body"> 
<a>Hello, this is a link</a> 
<a>Hello, this is a link, too</a> 
</div> 
</div> 
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html,'lxml')
print(soup.find_all(string=re.compile('link'))) 
>>>['Hello, this is a link', 'Hello, this is a link, too']

  • find 返回第一个元素,findall返回所有的列表 还有诸如 find_parents() find_parent() find_next_siblings() find_previous_siblings() find_all_previous() ....

4.3 CSS 选择器

  1. 标签选择器 Step:

  2. 转换数据类型 selector = parsel.Selector(html) #字符串 ———>对象

  3. css提取数据 p = selector.css('p') #提取p标签 .get():从提取到的对象中获取第一个元素(字符串) .getall():获取所有元素(列表)

  4. 类选择器

标签必须具有类属性 用.代表类属性 具有同类型的都会被提取 result = selector.css('.top').getall() 如果标签内有空格,要用.进行连接 空格表示子孙节点

  1. ID选择器 标识符为#,id一般唯一

  2. 组合选择器 其实就是塞一大坨

Capture 6 Ajax数据爬取

这是因为requests 获取的都是原始的HTML 文档,而浏览器中的页面则是经过JavaScript 处理数据后生成的结果,这些数据的 来源有多种,可能是通过Ajax 加载的、可能是包含在HTML文档中的,也可能是经过JavaScript和特 定算法计算后生成的。 分析网页后台向接口发送的Ajax 请求,如果可以用requests 来模拟Ajax 请求

6.1什么是Ajax

利用JavaScript 在保证页面不被刷新、页面链接不改变的情况下与服务器交换数据并更新 部分网页的技术。 发送Ajax 请求到网页更新的这个过程可以简单分为以下3 步: 1. 发送请求 * 新建了XMLHttpRequest 对象 * 调用onreadystatechange 属性设置了监听 * 调用open()和send()方法向某个链接(也就是服务器)发送了请求 * 所以当服务器返回响应时, onreadystatechange 对应的方法便会被触发,然后在这个方法里面解析响应内容即可。

  1. 解析内容 利用xmlhttp 的responseText 属性便可取到响应内容(类似于Python 中利用requests 向服务器发起请求,然后得到响应的过程) 返回内容可能是HTML ,可能是JSON。只需要在方法中用JavaScript 进一步处理即可

  2. 渲染网页 JavaScr刷有改变网页内容的能力,解析完响应内容之后,就可以调用JavaScript 来针对解析完的 内容对网页进行下一步处理了。

6.3 实战