路飞学城-Python 爬虫实战密训-第 1 章

本节学习体会:

鸡汤心得:
1、时代发展太快,要不断学习新东西,武装自己,才能跟得上时代的潮流,不然就是面临被pass的命运
2、要看清楚趋势,不要闭门造车
3、学习编程语言就跟学英语一样,方法很重要。总结为input-output-correction。
input:就是大量的视频、博客、书籍灌进去(每天坚持2小时看视频或者其他方式学习)
output:大量的练习,代码是敲出来的,不是看出来的。多去做题巩固。
correction:树木成长过程需要园艺工人修修剪剪,我们编程学习过程中也需要有人来指导修正。导师检查你的作业,指出一些学习方法与编程习惯的纠正。
爬虫笔记:
1、不会web能不能做爬虫?
答案:web与爬虫是不分家的。学web的一般都会一些反爬机制,学爬虫的对于web的前后端知识肯定是非常精通的,不然一段js都看不懂,会找不到入口的。
2、学爬虫会经历几个阶段:
首先是:找不着入口,不知道从何爬起。====》解决方法:多试几次,多摩擦几次。
然后是:反复的试错,试着试着就突然找到入口了。===》作总结
3、爬虫必备知识:
1、爬虫的本质,通过代码伪造浏览器行为发送请求。
2、Http请求伪造像不像要看以下两点:
- 请求头:(就是一个包装袋,描述里面是什么东西)
- user-agent: 代指用户使用的什么设备访问
- cookie:在用户浏览器上保存的标记。
- Host: 可以仿造浏览器里的headers
- refer: 防盗链有关
- origin: 跟浏览器一样就好
- 自定义的。。。
- 请求体:(就是袋子里面实际的东西)
- get请求的:name=alex&age=18
- post请求的:{"name":"alex","age":18}
3、分析Http请求
- chrome、firefox浏览器基本够用
- 专业的抓包工具
4、例子(爬取拉勾网,并修改个人信息)
爬取思路:
1、点击登录按钮,获取登录地址: https://passport.lagou.com/login/login.html
2、随便输入几个账号跟密码(输错,不要登录上),点击登录,点击浏览器的network,查看各个请求的请求头与请求体。
3、分析登录流程,整个请求流程,各个请求页的东西。===》不断试错
最后发现:
1、先获取token,发现在源码里面有自定义请求头里的Token和code:

window.X_Anti_Forge_Token = '07f11f54-4da9-4ea2-942c-4a72067e9b64';
window.X_Anti_Forge_Code = '36139046';

2、每一步重定向都有cookie返回(每次操作都更新一下cookie)

3、下一次操作是建立在上一次操作的submitcode的基础上。

4、建议先不要用session,先自己一步一步来,这个页面,那个页面,一个一个请求分析好后,知道每一步是做什么的时候,再去用session。

5、重定向:

这里A网站重定向到B
浏览器去访问A网站,A网站会返回一个location响应头给浏览器,这个location里面包含了重定向的地址,然后浏览器根据这个地址访问到了b网站


2、学习到的知识点总结


一、requests模块

http://www.cnblogs.com/wupeiqi/articles/6283017.html
https://www.cnblogs.com/lei0213/p/6957508.html
Python标准库中提供了:urllib、urllib2、httplib等模块以供Http请求,但是,它的 API 太渣了。它是为另一个时代、另一个互联网所创建的。它需要巨量的工作,甚至包括各种方法覆盖,来完成最简单的任务。
Requests 是使用 Apache2 Licensed 许可证的 基于Python开发的HTTP 库,其在Python内置模块的基础上进行了高度的封装,从而使得Pythoner进行网络请求时,变得美好了许多,使用Requests可以轻而易举的完成浏览器可有的任何操作。

1、安装

命令行用pip安装:
pip install requests

2、请求方式:

2.1、get请求
requests.get(url='x')

requests.request(method='get',url='x')
是一样的,因为get()的源码里面也是调用request(method='get',url='x')
无参数:
import requests
import json
r = requests.get("https://www.baidu.com/")
r.encoding = r.apparent_encoding    # 将编码格式设为网页原有的编码格式,避免中文乱码
# r.encoding = 'gbk'    # 设置输出的编码格式为gbk
print(r.url)    # 打印请求网址
print(r.text)   # 打印请求到的内容,一般为网页源码,为str格式
print(r.content)   # 打印请求到的内容,为bytes格式,二进制数据,需要转码,推荐使用:response.content.decode('utf8')的方式获取相应的html页面
# response.text返回的是Unicode格式,通常需要转换为utf-8格式,否则就是乱码。response.content是二进制模式,可以下载视频之类的,如果想看的话需要decode成utf-8格式。不管是通过response.content.decode("utf-8)的方式还是通过response.encoding="utf-8"的方式都可以避免乱码的问题发生
print(r.cookies)    # 查看cookies
print(r.status_code)  # 打印get请求的状态码,这里为200,表示请求成功
print(r.headers['content-type'])    # 查看请求的数据类型,这里是text/html,表示为html的文本类型
print(r.encoding)   # 查看编码格式,这里是utf-8
# 输出为json格式 
response = requests.get("http://httpbin.org/get")
print(type(response.text))  # <class 'str'>
print(response.json())  # 等同于json.loads(response.text)
print(json.loads(response.text))
print(type(response.json()))    # <class 'dict'>
有参数:
import requests
   
payload = {'key1': 'value1', 'key2': 'value2'}
ret = requests.get("http://httpbin.org/get", params=payload)
 
print(ret.url)  # 输出为:http://httpbin.org/get?key1=value1&key2=value2
# print(ret.text)
添加请求头headers

不加请求头:

import requests
  
url = 'https://www.zhihu.com/'
response = requests.get(url)
response.encoding = "utf-8"
print(response.text)
结果提示发生400错误(也就说你连知乎登录页面的html都下载不下来)
因为服务器把你认为是爬虫等恶意请求给拦截了,所以我们要添加headers来伪造成浏览器的请求
<html>
<head><title>400 Bad Request</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<hr><center>openresty</center>
</body>
</html>
所以我们按F12,查看network下的www.zhihu.com页面的headers,

代码如下:
import requests
  
url = 'https://www.zhihu.com/'
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36'
}
response = requests.get(url,headers=headers)
print(response.text)
2.2、post请求
通过post把数据提交到url地址,等同于一字典的形式提交form表单里面的数据,一般用于用户登录。
对于 POST 请求来说,我们一般需要为它增加一些参数。那么最基本的传参方法可以利用 data 这个参数。
1、传入表单,字典的方式。

一般这种方式,headers下面会有一个form data,如下:


import requests


response = requests.post(
    url='https://dig.chouti.com/login',
    data={
        'phone': '8615911111111',
        'password': '1314520',
        'oneMonth': '1'
    },
    headers={
        'user-agent': 'Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'},
    cookies=r1_cookie
)
print(response.text)
例二:
import requests
 
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.post("http://httpbin.org/post", data=payload)
print r.text
#运行结果如下:
{
"args": {}, 
"data": "", 
"files": {}, 
"form": {
"key1": "value1", 
"key2": "value2"
}, 
"headers": {
"Accept": "*/*", 
"Accept-Encoding": "gzip, deflate", 
"Content-Length": "23", 
"Content-Type": "application/x-www-form-urlencoded", 
"Host": "http://httpbin.org", 
"User-Agent": "python-requests/2.9.1"
}, 
"json": null, 
"url": "http://httpbin.org/post"
}
2、传入json数据
当浏览器里headers里的form data编程payload时,就是传入json数据。
有时候我们需要传送的信息不是表单形式的,需要我们传JSON格式的数据过去,所以我们可以用 json.dumps() 方法把表单数据序列化。
import json
import requests
 
url = 'http://httpbin.org/post'
payload = {'some': 'data'}
r = requests.post(url, data=json.dumps(payload))
print r.text
运行结果:
{
"args": {}, 
"data": "{\"some\": \"data\"}", 
"files": {}, 
"form": {}, 
"headers": {
"Accept": "*/*", 
"Accept-Encoding": "gzip, deflate", 
"Content-Length": "16", 
"Host": "http://httpbin.org", 
"User-Agent": "python-requests/2.9.1"
}, 
"json": {
"some": "data"
}, 
"url": "http://httpbin.org/post"
}
3、传入文件
如果想要上传文件,那么直接用 file 参数即可:
#新建一个 test.txt 的文件,内容写上 Hello World!
import requests
 
url = 'http://httpbin.org/post'
files = {'file': open('test.txt', 'rb')}
r = requests.post(url, files=files)
print r.text
 
{
"args": {}, 
"data": "", 
"files": {
"file": "Hello World!"
}, 
"form": {}, 
"headers": {
"Accept": "*/*", 
"Accept-Encoding": "gzip, deflate", 
"Content-Length": "156", 
"Content-Type": "multipart/form-data; boundary=7d8eb5ff99a04c11bb3e862ce78d7000", 
"Host": "http://httpbin.org", 
"User-Agent": "python-requests/2.9.1"
}, 
"json": null, 
"url": "http://httpbin.org/post"
}
这样我们便成功完成了一个文件的上传。
requests 是支持流式上传的,这允许你发送大的数据流或文件而无需先把它们读入内存。要使用流式上传,仅需为你的请求体提供一个类文件对象即可,非常方便:
with open('massive-body') as f:
requests.post('http://some.url/streamed', data=f)
2.3、其他请求:
r = requests.get("https://www.baidu.com") # 等效于requests.request(method='get', url='https://www.baidu.com')
requests.get(url, params=None, **kwargs)
requests.post(url, data=None, json=None, **kwargs)
requests.put(url, data=None, **kwargs)
requests.head(url, **kwargs)
requests.delete(url, **kwargs)
requests.patch(url, data=None, **kwargs)
requests.options(url, **kwargs)
# 以上方法均是在此方法的基础上构建
requests.request(method, url, **kwargs)

3、requests.session()会话保持

会话对象requests.Session能够跨请求地保持某些参数,比如cookies,即在同一个Session实例发出的所有请求都保持同一个cookies,而requests模块每次会自动处理cookies,这样就很方便地处理登录时的cookies问题。
不用session()的情况:
# 登录抽屉的例子:
import requests
from bs4 import BeautifulSoup


# 第一次访问任意页面获取cookies
r1 = requests.get(
    url='https://dig.chouti.com/all/hot/recent/1',
    headers={'user-agent':'Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'}
)
# print(r1.text)
r1_cookie=r1.cookies.get_dict()


# 第二次携带第一次的cookies登录,并对cookies授权,获取授权后的cookies,用授权后的cookies里的gpsd也可以
r2 = requests.post(
    url='https://dig.chouti.com/login',
    data={
        'phone': '8615918732559',
        'password': 'xxxxx',
        'oneMonth': '1'
    },
    headers={
        'user-agent': 'Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'},
    cookies=r1_cookie
)
r2_cookies = r2.cookies.get_dict() 


# 以后访问就都使用授权后的cookies了
vote = requests.post(
    url='https://dig.chouti.com/link/vote?linksId=%s' % id,
    headers={
        'user-agent': 'Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'},
    cookies=r2_cookies
)

用了session()会话保持后
用session对象取代requests去访问get请求或者post请求,就不用再担心cookies的问题了,它会自动更新cookies。
   
 import requests
 
    session = requests.Session()
 
    ### 1、首先登陆任何页面,获取cookie
 
    i1 = session.get(url="http://dig.chouti.com/help/service")
 
    ### 2、用户登陆,携带上一次的cookie,后台对cookie中的 gpsd 进行授权
    i2 = session.post(
        url="http://dig.chouti.com/login",
        data={
            'phone': "8615131255089",
            'password': "xxxxxx",
            'oneMonth': ""
        }
    )
 
    i3 = session.post(
        url="http://dig.chouti.com/link/vote?linksId=8589623",
    )
    print(i3.text)
session的一些基本设置:
# 用session对象发出get请求,设置cookies
session.get('http://httpbin.org/cookies/set/number/12456')
# 用session对象发出另外一个get请求,获取cookies
response = session.get('http://httpbin.org/cookies')

4、ssl证书认证:

现在随处可见 https 开头的网站,Requests可以为HTTPS请求验证SSL证书,就像web浏览器一样。要想检查某个主机的SSL证书,你可以使用 verify 参数,因为前段时间12306 证书不是无效的嘛,来测试一下:
无证书访问:
import requests
response = requests.get('https://www.12306.cn')
# 在请求https时,request会进行证书的验证,如果验证失败则会抛出异常
print(response.status_code)
结果爆出异常:

关闭证书验证
import requests
# 关闭验证,但是仍然会报出证书警告
response = requests.get('https://www.12306.cn',verify=False)
print(response.status_code)
还是报错:
E:\doubles\learnpython\venv\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)

200
消除验证证书的警报
from requests.packages import urllib3
import requests
  
urllib3.disable_warnings()
response = requests.get('https://www.12306.cn',verify=False)
print(response.status_code)

5、代理

import requests
  
proxies = {
  "http": "http://127.0.0.1:9743",
  "https": "https://127.0.0.1:9743",
}
response = requests.get("https://www.taobao.com", proxies=proxies)
print(response.status_code)

6、认证

如果碰到需要认证的网站可以通过requests.auth模块实现  
auth:用户输入用户名和密码后,浏览器会通过base64对用户名和密码加密再发送给服务端,通常是将用户名和密码加密为asdfadgdadsagljpoiqwjfl=,然后再在这个字符串前面加上base64和一个空格,发送到请求体,然后服务端会通过base64再将它解密出来。
HPPT_AUTHORIZATION:base asdfadgdadsagljpoiqwjfl=
import requests
from requests.auth import HTTPBasicAuth
#方法一
r = requests.get('http://120.27.34.24:9001', auth=HTTPBasicAuth('user', '123'))
#方法二<br>r = requests.get('http://120.27.34.24:9001', auth=('user', '123'))
print(r.status_code)

7、异常

import requests
from requests.exceptions import ReadTimeout, ConnectionError, RequestException
try:
    response = requests.get("http://httpbin.org/get", timeout = 0.5)
    print(response.status_code)
except ReadTimeout:
    print('Timeout')
except ConnectionError:
    print('Connection error')
except RequestException:
    print('Error')
首先被捕捉的异常是timeout,当把网络断掉的haul就会捕捉到ConnectionError,如果前面异常都没有捕捉到,最后也可以通过RequestExctption捕捉到

8、实例

下载一张图片:
import requests
 
response = requests.get('http://img.ivsky.com/img/tupian/pre/201708/30/kekeersitao-002.jpg')
b = response.content
with open('F://fengjing.jpg','wb') as f:
    f.write(b)

9、内置状态码

100: ('continue',),
101: ('switching_protocols',),
102: ('processing',),
103: ('checkpoint',),
122: ('uri_too_long', 'request_uri_too_long'),
200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/', '✓'),
201: ('created',),
202: ('accepted',),
203: ('non_authoritative_info', 'non_authoritative_information'),
204: ('no_content',),
205: ('reset_content', 'reset'),
206: ('partial_content', 'partial'),
207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'),
208: ('already_reported',),
226: ('im_used',),
 
# Redirection.
300: ('multiple_choices',),
301: ('moved_permanently', 'moved', '\\o-'),
302: ('found',),
303: ('see_other', 'other'),
304: ('not_modified',),
305: ('use_proxy',),
306: ('switch_proxy',),
307: ('temporary_redirect', 'temporary_moved', 'temporary'),
308: ('permanent_redirect',
      'resume_incomplete', 'resume',), # These 2 to be removed in 3.0
 
# Client Error.
400: ('bad_request', 'bad'),
401: ('unauthorized',),
402: ('payment_required', 'payment'),
403: ('forbidden',),
404: ('not_found', '-o-'),
405: ('method_not_allowed', 'not_allowed'),
406: ('not_acceptable',),
407: ('proxy_authentication_required', 'proxy_auth', 'proxy_authentication'),
408: ('request_timeout', 'timeout'),
409: ('conflict',),
410: ('gone',),
411: ('length_required',),
412: ('precondition_failed', 'precondition'),
413: ('request_entity_too_large',),
414: ('request_uri_too_large',),
415: ('unsupported_media_type', 'unsupported_media', 'media_type'),
416: ('requested_range_not_satisfiable', 'requested_range', 'range_not_satisfiable'),
417: ('expectation_failed',),
418: ('im_a_teapot', 'teapot', 'i_am_a_teapot'),
421: ('misdirected_request',),
422: ('unprocessable_entity', 'unprocessable'),
423: ('locked',),
424: ('failed_dependency', 'dependency'),
425: ('unordered_collection', 'unordered'),
426: ('upgrade_required', 'upgrade'),
428: ('precondition_required', 'precondition'),
429: ('too_many_requests', 'too_many'),
431: ('header_fields_too_large', 'fields_too_large'),
444: ('no_response', 'none'),
449: ('retry_with', 'retry'),
450: ('blocked_by_windows_parental_controls', 'parental_controls'),
451: ('unavailable_for_legal_reasons', 'legal_reasons'),
499: ('client_closed_request',),
 
# Server Error.
500: ('internal_server_error', 'server_error', '/o\\', '✗'),
501: ('not_implemented',),
502: ('bad_gateway',),
503: ('service_unavailable', 'unavailable'),
504: ('gateway_timeout',),
505: ('http_version_not_supported', 'http_version'),
506: ('variant_also_negotiates',),
507: ('insufficient_storage',),
509: ('bandwidth_limit_exceeded', 'bandwidth'),
510: ('not_extended',),
511: ('network_authentication_required', 'network_auth', 'network_authentication'),
状态码用法
import requests
response = requests.get('http://www.jianshu.com/404.html')
# 使用request内置的字母判断状态码
  
#如果response返回的状态码是非正常的就返回404错误
if response.status_code != requests.codes.ok:
    print('404')
  
#如果页面返回的状态码是200,就打印下面的状态
response = requests.get('http://www.jianshu.com')
if response.status_code == 200:
    print('200')

二、Beautifulsoup模块

https://cuiqingcai.com/1319.html
前面我们用requests模块下载了网页,接下来就是对网页的解析了,我们可以用正则提取网页里的一些有用信息,但是正则用起来很麻烦,匹配的精准度也不够,所以我们一般使用模块,比如Beautufulsoup模块。

1、安装

建议安装Beautifulsoup4,使用pip安装如下:
pip install beautifulsoup4 

2、使用示例

from bs4 import BeautifulSoup
 
html = '''
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><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">...</p>
'''
soup = BeautifulSoup(html,'lxml') # 实例化一个soup对象,html是之前下载的html代码,'lxml'是解析器,需要安装,python内置的是html.parser解析器
print(soup.prettify())	# 格式化输出,更美观
print(soup.title)	# 获取第一个title标签,<title>The Dormouse's story</title>
print(soup.title.name)	# 获取第一个title标签名,结果是title
print(soup.title.string)	# 获取第一个title的内容,The Dormouse's story
print(soup.title.parent.name)	# 获取第一个title的父标签名,head
print(soup.p)		# 获取第一个p标签,<p class="title"><b>The Dormouse's story</b></p>
print(soup.p["class"])	# 获取第一个p标签的class属性,["title"]
print(soup.a)		# 获取第一个a标签,<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>
print(soup.find_all('a'))	# 找出所有的a标签,返回一个列表
print(soup.find(id='link3'))	# 找出第一个id为'link3'的标签。

3、各属性用法

获取标签名name:
soup.title.name #可以获得该title标签的名称,即title
标签属性attrs
标签名也是属性的一部分,可以通过属性来获取标签名
soup.p.attrs['name']	# 获取p标签的名字即p
soup.p.attrs['id']	# 获取p标签id
soup.p.attrs['class']		# 获取p标签的样式class
标签内容string
soup.p.string	# 结果就可以获取第一个p标签的内容:The Dormouse's story
也可以嵌套获取
soup.head.title.p.string	# 结果同soup.p.string,
获取子标签,
孙标签也是嵌在子标签里一起输出的
法一:
print(soup.p.contents)	# 获取p节点里面的所有节点,并以列表的形式返回
法二:
print(soup.p.children)	# 获取p节点里面的所有节点,并以迭代器iter的形式返回,只能通过循环迭代获取
比如:
for i,child in enumerate(soup.p.children):
    print(i,child)
获取子子孙孙标签,每个子标签跟孙标签都作为一条记录获取
print(soup.p.descendants)
父节点和祖先节点
通过soup.a.parent就可以获取父节点的信息
通过list(enumerate(soup.a.parents))可以获取祖先节点,这个方法返回的结果是一个列表,会分别将a标签的父节点的信息存放到列表中,以及父节点的父节点也放到列表中,并且最后还会讲整个文档放到列表中,所有列表的最后一个元素以及倒数第二个元素都是存的整个文档的信息
兄弟节点
soup.a.next_siblings 获取后面的兄弟节点
soup.a.previous_siblings 获取前面的兄弟节点
soup.a.next_sibling 获取下一个兄弟标签
souo.a.previous_sinbling 获取上一个兄弟标签

4、find_all

find_all(name,attrs,recursive,text,**kwargs)
可以根据标签名,属性,内容查找文档
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('ul'))	# 查找所有的ul标签,并以列表的形式返回
print(type(soup.find_all('ul')[0]))	# 查找所有的ul标签,返回列表里的第一个ul标签。
同时我们是可以针对结果再次find_all,从而获取所有的li标签信息
for ul in soup.find_all('ul'):
    print(ul.find_all('li'))
# 通过attrs属性查找
print(soup.find_all(attrs={'id': 'list-1'}))
print(soup.find_all(attrs={'name': 'elements'}))
attrs可以传入字典的方式来查找标签,但是这里有个特殊的就是class,因为class在python中是特殊的字段,所以如果想要查找class相关的可以更改attrs={'class_':'element'}或者soup.find_all('',{"class":"element}),特殊的标签属性可以不写attrs,例如id
通过标签文本查找
print(soup.find_all(text='Foo'))
返回的结果是所有Foo的文本。

5、find(用法与find_all差不多)

find(name,attrs,recursive,text,**kwargs)
find返回的匹配结果的第一个元素
其他一些类似的用法:
find_parents()返回所有祖先节点,find_parent()返回直接父节点。
find_next_siblings()返回后面所有兄弟节点,find_next_sibling()返回后面第一个兄弟节点。
find_previous_siblings()返回前面所有兄弟节点,find_previous_sibling()返回前面第一个兄弟节点。
find_all_next()返回节点后所有符合条件的节点, find_next()返回第一个符合条件的节点
find_all_previous()返回节点后所有符合条件的节点, find_previous()返回第一个符合条件的节点

6、select(通过css查找标签)

通过select()直接传入CSS选择器就可以完成选择
熟悉前端的人对CSS可能更加了解,其实用法也是一样的
.表示class #表示id
标签1,标签2 找到所有的标签1和标签2
标签1 标签2 找到标签1内部的所有的标签2
[attr] 可以通过这种方法找到具有某个属性的所有标签
[atrr=value] 例子[target=_blank]表示查找所有target=_blank的标签


from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.select('.panel .panel-heading'))	# 查找class为panel下面,所有class为panel-heading的标签
print(soup.select('ul li'))	# 查找ul标签下所有的li标签
print(soup.select('#list-2 .element'))	# 查找id为list-2,所有class为element的标签
print(type(soup.select('ul')[0]))	# 查找ul标签下的第一个子标签
# 获取文本
for li in soup.select('li'):
    print(li.get_text())
# 获取属性
# 获取属性的时候可以通过[属性名]或者attrs[属性名]
for ul in soup.select('ul'):
    print(ul['id'])
    print(ul.attrs['id'])

Beautifulsoup总结:

推荐使用lxml解析库,必要时使用html.parser
标签选择筛选功能弱但是速度快
建议使用find()、find_all() 查询匹配单个结果或者多个结果
如果对CSS选择器熟悉建议使用select()
记住常用的获取属性和文本值的方法

登录示例:

1、抽屉登录并点赞

import requests
from bs4 import BeautifulSoup

# 访问页面获取源码与cookies,注意携带headers,伪造的更像浏览器
r1 = requests.get(
    url='https://dig.chouti.com/all/hot/recent/1',
    headers={'user-agent':'Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'}
)
r1_cookie=r1.cookies.get_dict()

# 分析源码,获取每篇文章的链接id
soup = BeautifulSoup(r1.text,'html.parser')
div = soup.find('div', 'content-list')
item_list = div.find_all(attrs={'class': 'item'})
id_list = []
for item in item_list:
    tag = item.find(name='div', attrs={'class': 'part2'})
    # link_id = link_div.attrs['share-linkid']
    link_id = tag.get('share-linkid')
    id_list.append(link_id)
    # print(link_id)

# 携带headers与之前的cookies,登录抽屉,并对cookies授权
r2 = requests.post(
    url='https://dig.chouti.com/login',
    data={
        'phone': '86159xxxxxxxx',
        'password': 'xxxxxx',
        'oneMonth': '1'
    },
    headers={
        'user-agent': 'Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'},
    cookies=r1_cookie
)
r2_cookies = r2.cookies.get_dict()

# 携带授权后的cookies,对文章点赞
for id in id_list:
    vote = requests.post(
        url='https://dig.chouti.com/link/vote?linksId=%s' % id,
        headers={
            'user-agent': 'Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'},
        cookies=r2_cookies
    )

    vote_cancel = requests.post(
        url='https://dig.chouti.com/vote/cancel/vote.do',
        data={
            'linksId':'%s' % id
        },
        headers={
            'user-agent': 'Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'},
        cookies=r1_cookie
    )

    print(vote_cancel.text)

2、自动登录github并获取个人信息

import requests
from bs4 import BeautifulSoup

# 第一步,获取token
get_token_html = requests.get('https://github.com/login')
get_token_soup = BeautifulSoup(get_token_html.text, 'html.parser')
# 因为input标签的内容是空的,所以不要用hidden_tag.text,它是个空值。
token = get_token_soup.find(name='input',attrs={'name': 'authenticity_token'}) .get('value')
# 获取cookies,此时未授权
cookies_list=get_token_html.cookies.get_dict()
# print(token)
# print(ret1_cookies)

# 第二步,携带步骤一的cookies和token登录github
sign_in_html = requests.post(
    url='https://github.com/session',
    data={
        'utf8': '✓',
        'authenticity_token': '%s' % token,
        'login': 'gituser',
        'password': 'gitpassword',
        'commit': 'Sign in'
    },
    headers={'User-Agent': 'Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'},
    cookies=cookies_list
)
# 获取登录后授权的cookies
sign_in_cookies = sign_in_html.cookies.get_dict()

# print(sign_in_html.text)
# 通过登录后的返回页获取用户名
sign_in_soup = BeautifulSoup(sign_in_html.text, 'lxml')
username = sign_in_soup.find('strong', attrs={'class': 'css-truncate-target'}).text
print('用户信息之用户名是:', username)

# 第三步,进入个人信息页
profile_html = requests.get(
    url='https://github.com/%s'%username,
    headers={'User-Agent': 'Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'},
    cookies=sign_in_cookies
)

profile_soup = BeautifulSoup(profile_html.text, 'lxml')
# 下载用户头像
avatar_url = profile_soup.find('img', attrs={'class': 'avatar width-full rounded-2'}).get('src')
avatar_filename = '%s_avatar.jpg'%username
with open(avatar_filename,'wb') as f:
    avatar_img = requests.get(avatar_url)
    f.write(avatar_img.content)
print('已下载用户头像在本地!')

# 获取用户仓库信息
repositories_html = requests.get('https://github.com/%s?tab=repositories'%username)
# print(repositories_html.text)
repositories_soup = BeautifulSoup(repositories_html.text, 'lxml')
repositories_div = repositories_soup.find(name='div', id='user-repositories-list')
repositories_li_list = repositories_div.find_all('li')
for li in repositories_li_list:
    tag = li.find(name='a', attrs={'itemprop': 'name codeRepository'})
    repositories_name = tag.text.strip()
    repositories_href = 'https://github.com%s' % tag.get('href')
    print('用户仓库名:%s,仓库链接地址:%s'%(repositories_name,repositories_href))



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值