python3 爬虫相关-requests和BeautifulSoup

前言

时间的关系,这篇文章只记录了相关库的使用,没有进行深入分析,各位看官请见谅(还是因为懒。。。。。)

requests使用

发送无参数的get请求

r = requests.get('http://httpbin.org/get')
print(r.text)

发送带参数的get请求

load = {'key1': 'value1', 'key2': 'value2'}
r = requests.get("http://httpbin.org/get",params = load)
print(r.url)
u'http://httpbin.org/get?key2=value2&key1=value1'

发送带参数的post请求

想要发送一些编码为表单形式的数据,表现形式非常像一个HTML表单。要实现这个,只需简单地传递一个字典给 data 参数。你的数据字典 在发出请求时会自动编码为表单形式。

load = {'key1': 'value1', 'key2': 'value2'}
r = requests.post("http://httpbin.org/post", data=load)
print(r.text)

{
  ...
  "form": {
    "key2": "value2",
    "key1": "value1"
  },
  ...
}

很多时候你想要发送的数据并非编码为表单形式的。如果你传递一个string而不是一个dict,那么数据会被直接发布出去。

例如,Github API 3接受编码为JSON的POST/PATCH数据:

>>> url = 'https://api.github.com/some/endpoint'
>>> load = {'some': 'data'}
>>> r = requests.post(url, data=json.dumps(load))

发送文件的post类型

这个相当于向网站上传一张图片,文档等操作,这时要使用files参数

url = 'http://httpbin.org/post'
files = {'file': open('touxiang.png', 'rb')}
r = requests.post(url, files=files)

定制headers,使用headers参数来传递

url = 'https://api.github.com/some/endpoint'
load = {'some': 'data'}
headers = {'content-type': 'application/json'}
r = requests.post(url, data=json.dumps(load), headers=headers)

编码类型

可以找出Requests使用了什么编码,并且能够改变它

r.encoding
'utf-8'
r.encoding = 'ISO-8859-1'

如果改变了编码,每当访问r.text时,Request都将会使用r.encoding的新值。

响应内容

响应状态码

r = requests.get(‘http://httpbin.org/get‘)
print(r.status_code)

响应头

print(r.headers)

也可以取到这个个别的响应头用来做一些判断,这里的参数是不区分大小写的

r.headers[‘Content-Type’]
r.headers.get(‘Content-Type’)

响应内容

# 被encoding解码后内容
r.text
# 以字节的方式访问请求响应体
r.content

获取响应中的cookies

r = requests.get('http://www.baidu.com')
r.cookies['BAIDUID']

也可以自已定义请求的COOKIES

url = 'http://httpbin.org/cookies'
cookies = {'cookies_are':'working'}
r = requests.get(url,cookies = cookies)
print(r.text)
{
  "cookies": {
    "cookies_are": "working"
  }
}

设置超时时间

requests.get('http://github.com', timeout=1)

访问中使用session

先初始化一个session对象

s = requests.Session()

然后使用这个session对象来进行访问

r = s.post(url,data = user)

JSON相应内容

requests中也有一个内置的JSON解码器,助你处理JSON数据

r = requests.get('https://github.com/timeline.json')
r.json()
[{u'repository': {u'open_issues': 0, u'url': 'https://github.com/...

如果JSON解码失败,r.json就会抛出一个异常

原始响应内容

在罕见的情况下你可能想获取来自服务器的原始套接字响应,那么你可以访问r.raw 如果你确实想这么干,那请你确保在初始请求中设置了stream=True

>>> r = requests.get('https://github.com/timeline.json', stream=True)
>>> r.raw
<requests.packages.urllib3.response.HTTPResponse object at 0x101194810>
>>> r.raw.read(10)
'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'

错误与异常

遇到网络问题(如:DNS查询失败、拒绝连接等)时,Requests会抛出一个ConnectionError 异常。

遇到罕见的无效HTTP响应时,Requests则会抛出一个 HTTPError 异常。

若请求超时,则抛出一个 Timeout 异常。

若请求超过了设定的最大重定向次数,则会抛出一个 TooManyRedirects 异常。

所有Requests显式抛出的异常都继承自 requests.exceptions.RequestException 。

Beautiful Soup使用方法

创建 beautifulsoup 对象

soup = BeautifulSoup(html)

另外,我们还可以用本地 HTML 文件来创建对象,例如

soup = BeautifulSoup(open('index.html'))

上面这句代码便是将本地 index.html 文件打开,用它来创建 soup 对象

下面我们来打印一下 soup 对象的内容,格式化输出

print(soup.prettify())

四大对象种类

Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:

  • Tag
  • NavigableString
  • BeautifulSoup
  • Comment
Tag

Tag 通俗点讲就是 HTML 中的一个个标签

打印标题标签内容

print(soup.title)
#<title>The Dormouse's story</title>

打印属性

print(soup.a.attrs)
{'href': '#main', 'class': ['skip-link', 'screen-reader-text']}

打印特定属性

print(soup.a['class'])
['skip-link', 'screen-reader-text']
或者
print(soup.a.get('href'))
#main

修改属性

soup.a['href']='#'
print(soup.a)

<a class="skip-link screen-reader-text" href="#">Skip to content</a>

删除属性

del soup.a['class']
print(soup.a)

<a href="#main">Skip to content</a>

用 .string 即可获取标签内部的文字,它的类型是一个NavigableString

print(soup.a.string)

Skip to content
BeautifulSoup

BeautifulSoup对象表示的是一个文档的全部内容.大部分时候,可以把它当作Tag对象,是一个特殊的Tag,我们可以分别获取它的类型,名称,以及属性

Comment

Comment对象是一个特殊类型的NavigableString对象,其实输出的内容仍然不包括注释符号,但是如果不好好处理它,可能会对我们的文本处理造成意想不到的麻烦。

遍历文档树

直接子节点
  • .contents

    tag 的 .content 属性可以将tag的子节点以列表的方式输出

    print(soup.a.contents)
    
    ['Skip to content']
    
  • .children

    它返回的不是一个list,不过我们可以通过遍历获取所有子节点。

    我们打印输出.children看一下,可以发现它是一个list生成器对象

所有子孙节点

.descendants

.contents和.children属性仅包含tag的直接子节点,.descendants属性可以对所有tag的子孙节点进行递归循环,和children类似,我们也需要遍历获取其中的内容。

for child in soup.descendants:
    print("------->")
    print(child)
节点内容

如果tag只有一个NavigableString类型子节点,那么这个tag可以使用.string得到子节点。如果一个tag仅有一个子节点,那么这个tag也可以使用.string方法,输出结果与当前唯一子节点的.string 结果相同。

如果tag包含了多个子节点,tag就无法确定,string方法应该调用哪个子节点的内容,.string的输出结果是None

多个内容

.strings获取多个内容,不过需要遍历获取

for string in soup.head.strings:
    print("------->")
    print(string)

.stripped_strings

输出的字符串中可能包含了很多空格或空行,使用.stripped_strings可以去除多余空白内容

for string in soup.head.stripped_strings:
    print("------->")
    print(string)
父节点
p=soup.p
print(p.parent.name)
全部父节点

.parents

通过元素的.parents属性可以递归得到元素的所有父辈节点,例如

p=soup.p
for parent in p.parents:
    print('------->')
    print(parent.name)
兄弟节点

兄弟节点可以理解为和本节点处在统一级的节点,.next_sibling属性获取了该节点的下一个兄弟节点,.previous_sibling则与之相反,如果节点不存在,则返回None

注意:实际文档中的tag的.next_sibling和.previous_sibling属性通常是字符串或空白,因为空白或者换行也可以被视作一个节点,所以得到的结果可能是空白或者换行

全部兄弟节点

通过.next_siblings和.previous_siblings属性可以对当前节点的兄弟节点迭代输出

前后节点

.next_element和.previous_element属性,与.next_sibling和.previous_sibling不同,它并不是针对于兄弟节点,而是在所有节点,不分层次

所有前后节点

通过.next_elements和.previous_elements的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样

搜索文档树

find_all( name , attrs , recursive , text , **kwargs )

find_all() 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件

  1. name 参数

name参数可以查找所有名字为name的tag,字符串对象会被自动忽略掉

  1. keyword 参数

如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup会搜索每个tag的”id”属性

3.text 参数

通过 text 参数可以搜搜文档中的字符串内容.与 name 参数的可选值一样, text 参数接受 字符串 , 正则表达式 , 列表, True

4.limit 参数

find_all()方法返回全部的搜索结构,如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用limit参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果.

5.recursive 参数

调用tag的find_all()方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False .

find( name , attrs , recursive , text , **kwargs )

它与find_all()方法唯一的区别是find_all()方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果

find_parents()/find_parent()

find_all() 和 find() 只搜索当前节点的所有子节点,孙子节点等. find_parents() 和 find_parent() 用来搜索当前节点的父辈节点,搜索方法与普通tag的搜索方法相同,搜索文档搜索文档包含的内容

find_next_siblings()/find_next_sibling()

这2个方法通过 .next_siblings 属性对当 tag 的所有后面解析的兄弟 tag 节点进行迭代, find_next_siblings() 方法返回所有符合条件的后面的兄弟节点,find_next_sibling() 只返回符合条件的后面的第一个tag节点

find_previous_siblings()/find_previous_sibling()

这2个方法通过 .previous_siblings 属性对当前 tag 的前面解析的兄弟 tag 节点进行迭代, find_previous_siblings() 方法返回所有符合条件的前面的兄弟节点, find_previous_sibling() 方法返回第一个符合条件的前面的兄弟节点

find_all_next()/find_next()

这2个方法通过 .next_elements 属性对当前 tag 的之后的 tag 和字符串进行迭代, find_all_next() 方法返回所有符合条件的节点, find_next() 方法返回第一个符合条件的节点

find_all_previous() 和 find_previous()

这2个方法通过 .previous_elements 属性对当前节点前面的 tag 和字符串进行迭代, find_all_previous() 方法返回所有符合条件的节点, find_previous()方法返回第一个符合条件的节点

CSS选择器

我们在写CS 时,标签名不加任何修饰,类名前加点,id名前加#,在这里我们也可以利用类似的方法来筛选元素,用到的方法是soup.select(),返回类型是list

  1. 通过标签名查找
print soup.select('title') 
#[<title>The Dormouse's story</title>]

print soup.select('a')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

print soup.select('b')
#[<b>The Dormouse's story</b>]
  1. 通过类名查找
print soup.select('.sister')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
  1. 通过 id 名查找
print soup.select('#link1')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]
  1. 组合查找
    组合查找即和写css文件时,标签名与类名、id名进行的组合原理是一样的,例如查找p 标签中,id等于link1的内容,二者需要用空格分开
print soup.select('p #link1')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]

直接子标签查找
print soup.select("head > title")

[The Dormouse's story]

  1. 属性查找
    查找时还可以加入属性元素,属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。
print soup.select('a[class="sister"]')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

print soup.select('a[href="http://example.com/elsie"]')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]

同样,属性仍然可以与上述查找方式组合,不在同一节点的空格隔开,同一节点的不加空格

print soup.select('p a[href="http://example.com/elsie"]')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]

以上的 select 方法返回的结果都是列表形式,可以遍历形式输出,然后用 get_text() 方法来获取它的内容。

soup = BeautifulSoup(html, 'lxml')
print type(soup.select('title'))
print soup.select('title')[0].get_text()

for title in soup.select('title'):
    print title.get_text()
posted @ 2018-07-05 01:07  cos1eqlg0  阅读(285)  评论(0编辑  收藏  举报