爬了个小虫

爬了个小虫

requests

零之简介

介绍: 使用requests可以模拟浏览器的请求,比起之前用到的urllibrequests模块的api更加便捷(本质就是封装了urllib3

注意:requests库发送请求将网页内容下载下来以后,并不会执行js代码,这需要我们自己分析目标站点然后发起新的request请求

官网链接: http://docs.python-requests.org/en/master/

一之GET请求

请求头的User-Agent字段伪装成浏览器,并用urlencode将中文转码放入get请求

#如果查询关键词是中文或者有其他特殊符号,则不得不进行url编码
from urllib.parse import urlencode, unquote  # unquote则是将url编码的解码成中文
wd='egon老师'
encode_res=urlencode({'k':wd},encoding='utf-8')
keyword=encode_res.split('=')[1]
print(keyword)
# 然后拼接成url
url='https://www.baidu.com/s?wd=%s&pn=1' %keyword

response=requests.get(url,
                      headers={
                        'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36',
                      })
res1=response.text

但实际上可以用params参数一并完成转码(requests封装的urlencode)

response=requests.get('https://www.baidu.com/s',
                      params={
                          'wd':wd,
                          'pn':pn
                      },
                      headers={
                        'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36',
                      })

url编码解码用unquote

res=unquote('%E7%87%95%E7%AA%9D')
print(res)

GET的header参数=>各种请求头

#通常我们在发送请求时都需要带上请求头,请求头是将自身伪装成浏览器的关键,常见的有用的请求头如下
Host

Referer #大型网站通常都会根据该参数判断请求来源的网址

User-Agent #客户端的型号,其中还会区分不同终端,如手机、电脑、平板

Cookie #Cookie信息虽然包含在请求头里,但requests模块有单独的参数来处理他,headers={}内就不要放它了(该字段只能放入字符串类型的cookie)

浏览器会识别请求头,不加可能会被拒绝访问


GET的cookies参数

cookie参数中要传入一个字典形式中包含许多字典形式的cookie
示例:用cookie自动登录github

#登录github,然后从浏览器中获取cookies,以后就可以直接拿着cookie登录了,无需输入用户名密码
#用户名:egonlin 邮箱378533872@qq.com 密码lhf@123

import requests

Cookies={   'user_session':'wGMHFJKgDcmRIVvcA14_Wrt_3xaUyJNsBnPbYzEL6L0bHcfc',
}

response=requests.get('https://github.com/settings/emails',
             cookies=Cookies) #github对请求头没有什么限制,我们无需定制user-agent,对于其他网站可能还需要定制

print('378533872@qq.com' in response.text) #True

二之POST请求

对比GET、POST

HTTP默认的请求方法就是GET
* 没有请求体
* 数据必须在1K之内!
* GET请求数据会暴露在浏览器的地址栏中

GET请求常用的操作:
1. 在浏览器的地址栏中直接给出URL,那么就一定是GET请求
2. 点击页面上的超链接也一定是GET请求
3. 提交表单时,表单默认使用GET请求,但可以设置为POST

#POST请求
(1). 数据不会出现在地址栏中
(2). 数据的大小没有上限
(3). 有请求体
(4). 请求体中如果存在中文,会使用URL编码!

!!!requests.post()用法与requests.get()完全一致,特殊的是requests.post()有一个data参数,用来存放请求体数据


发送post请求,模拟浏览器的登录行为

抓包:需要用错误的用户名或密码登录(对于登录来说,应该输错用户名或密码然后分析抓包流程,用脑子想一想,输对了浏览器就跳转了,还分析个毛线,累死你也找不到包)

案例:github抓包并获取cookie

一 目标站点分析
    浏览器输入https://github.com/login
    然后输入错误的账号密码,抓包
    发现登录行为是post提交到:https://github.com/session
    而且请求头包含cookie
    而且请求体包含:
        commit:Sign in
        utf8:✓
        authenticity_token:lbI8IJCwGslZS8qJPnof5e7ZkCoSoMn6jmDTsL1r/m06NLyIbw7vCrpwrFAPzHMep3Tmf/TSJVoXWrvDZaVwxQ==
        login:egonlin
        password:123



二 流程分析
    先GET:https://github.com/login拿到初始cookie与authenticity_token
    返回POST:https://github.com/session, 带上初始cookie,带上请求体(authenticity_token,用户名,密码等)
    最后拿到登录cookie

ps:如果密码时密文形式,则可以先输错账号,输对密码,然后到浏览器中拿到加密后的密码,这里github传输的密码是明文

import requests
import re

#第一次请求
r1=requests.get('https://github.com/login')
r1_cookie=r1.cookies.get_dict() #拿到初始cookie(未被授权)
authenticity_token=re.findall(r'name="authenticity_token".*?value="(.*?)"',r1.text)[0] #从页面中拿到CSRF TOKEN

#第二次请求:带着初始cookie和TOKEN发送POST请求给登录页面,带上账号密码
data={
    'commit':'Sign in',
    'utf8':'✓',
    'authenticity_token':authenticity_token,
    'login':'317828332@qq.com',
    'password':'alex3714'
}
r2=requests.post('https://github.com/session',
             data=data,
             cookies=r1_cookie
             )

login_cookie=r2.cookies.get_dict()

#第三次请求:以后的登录,拿着login_cookie就可以,比如访问一些个人配置
r3=requests.get('https://github.com/settings/emails',
                cookies=login_cookie)

print('317828332@qq.com' in r3.text) #True

requests.session()自动保存cookie


import requests
import re

session=requests.session()
#第一次请求
r1=session.get('https://github.com/login')
authenticity_token=re.findall(r'name="authenticity_token".*?value="(.*?)"',r1.text)[0] #从页面中拿到CSRF TOKEN

#第二次请求
data={
    'commit':'Sign in',
    'utf8':'✓',
    'authenticity_token':authenticity_token,
    'login':'317828332@qq.com',
    'password':'alex3714'
}
r2=session.post('https://github.com/session',
             data=data,
             )

#第三次请求
r3=session.get('https://github.com/settings/emails')

print('317828332@qq.com' in r3.text) #True

请求体中url编码格式与json格式

requests.post(url='xxxxxxxx',
              data={'xxx':'yyy'}) #没有指定请求头,#默认的请求头:application/x-www-form-urlencode
#如果我们自定义请求头是application/json,并且用data传值, 则服务端取不到值
requests.post(url='',
              data={'':1,},
              headers={
                  'content-type':'application/json'
              })

requests.post(url='',
              json={'':1,},
              ) #默认的请求头:application/json

三之响应Response

respone属性

respone.text # html的文本内容
respone.content # 整页的二进制数据,可以用来取得视频图片
response.iter_content # 二进制流,分段式的传输过来,如较大的视频
respone.status_code # 状态码
respone.headers # 响应头
respone.cookies # cookie对象 RequestsCookieJar的对象
from requests.cookies import RequestsCookieJar # 在pycharm中用此方法点进去看源码
respone.cookies.get_dict() # cookie对象转成字典
respone.cookies.items() # 遍历cookie
respone.url # 请求地址
respone.history # 网站中各种重定向后,之前的地址的历史记录
respone.encoding # 网站编码(大部分网站都是用的utf-8,但还是会有很少一部分用的gb2312这种,requests的默认编码为ISO-8859-1,如果不设置成gbk则中文乱码:response.encoding='gbk')

RequestsCookieJar载入cookie

from requests.cookies import RequestsCookieJar

jar = RequestsCookieJar()
with open("cookie.json", "r") as fp:
    cookies = json.load(fp)
    for cookie in cookies:
        jar.set(cookie['name'], cookie['value'])

之后就可以在请求中加入cookies=jar来装填cookie了

requests.post(url,cookies=jar,headers=header,data=data)

获取二进制数据

import requests

response=requests.get('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1509868306530&di=712e4ef3ab258b36e9f4b48e85a81c9d&imgtype=0&src=http%3A%2F%2Fc.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F11385343fbf2b211e1fb58a1c08065380dd78e0c.jpg')

with open('a.jpg','wb') as f:
    f.write(response.content)

获取二进制流

#stream参数:一点一点的取,比如下载视频时,如果视频100G,用response.content然后一下子写到文件中是不合理的

import requests

response=requests.get('https://gss3.baidu.com/6LZ0ej3k1Qd3ote6lo7D0j9wehsv/tieba-smallvideo-transcode/1767502_56ec685f9c7ec542eeaf6eac93a65dc7_6fe25cd1347c_3.mp4',
                      stream=True)

with open('b.mp4','wb') as f:
    for line in response.iter_content():
        f.write(line)

requests封装的解析json

#解析json
import requests
response=requests.get('http://httpbin.org/get')

import json
res1=json.loads(response.text) #太麻烦

res2=response.json() #直接获取json数据

print(res1 == res2) #True

allow_redirects 与 history

allow_redirects是requests发送请求时的属性
配置allow_redirects=false时遇到重定向也不会跳转,并且response.history也是个空列表
而没有配置时遇到重定向会正常跳转,response.history中也能取到历史对象


requests的高级用法

#证书验证(大部分网站都是https)
import requests
respone=requests.get('https://www.12306.cn') #如果是ssl请求,首先检查证书是否合法,不合法则报错,程序终端

#改进1:去掉报错,但是会报警告
import requests
respone=requests.get('https://www.12306.cn',verify=False) #不验证证书,报警告,返回200
print(respone.status_code)

#改进2:去掉报错,并且去掉警报信息
import requests
from requests.packages import urllib3
urllib3.disable_warnings() #关闭警告
respone=requests.get('https://www.12306.cn',verify=False)
print(respone.status_code)

#改进3:加上证书
#很多网站都是https,但是不用证书也可以访问,大多数情况都是可以携带也可以不携带证书
#知乎\百度等都是可带可不带
#有硬性要求的,则必须带,比如对于定向的用户,拿到证书后才有权限访问某个特定网站
import requests
respone=requests.get('https://www.12306.cn',
                     cert=('/path/server.crt',
                           '/path/key'))
print(respone.status_code)

使用代理

官网链接: http://docs.python-requests.org/en/master/user/advanced/#proxies

代理设置:先发送请求给代理,然后由代理帮忙发送(封ip是常见的事情)

import requests
proxies={
    'http':'http://egon:123@localhost:9743',#带用户名密码的代理,@符号前是用户名与密码
    'http':'http://localhost:9743',
    'https':'https://localhost:9743',
}
respone=requests.get('https://www.12306.cn',
                     proxies=proxies)

print(respone.status_code)
#支持socks代理,安装:pip install requests[socks]
import requests
proxies = {
    'http': 'socks5://user:pass@host:port',
    'https': 'socks5://user:pass@host:port'
}
respone=requests.get('https://www.12306.cn',
                     proxies=proxies)

print(respone.status_code)

高匿和透明
高匿:服务端很难取到真实的ip
透明:服务端可以取到真实的ip地址,从请求头中:X-Forwarded-For取到,Django就是从Meta中取
代理池:搞一堆代理,放到列表中,每次发请求,随机出一个(开源代理池)

ps:自己写一个django,从中用META.get('REMOTE_ADDR')查看访问的IP,看看代理后访问的IP还是原本IP吗

超时设置

#超时设置
#两种超时:float or tuple
#timeout=0.1 #代表接收数据的超时时间
#timeout=(0.1,0.2)#0.1代表链接超时  0.2代表接收数据的超时时间

import requests
respone=requests.get('https://www.baidu.com',
                     timeout=0.0001)

认证设置(现在已经很少用上了,了解即可)

官网链接: http://docs.python-requests.org/en/master/user/authentication/

认证设置:登陆网站是,弹出一个框,要求你输入用户名密码(与alter很类似),此时是无法获取html的
但本质原理是拼接成请求头发送

#         r.headers['Authorization'] = _basic_auth_str(self.username, self.password)
# 一般的网站都不用默认的加密方式,都是自己写
# 那么我们就需要按照网站的加密方式,自己写一个类似于_basic_auth_str的方法
# 得到加密字符串后添加到请求头
#         r.headers['Authorization'] =func('.....')

#看一看默认的加密方式吧,通常网站都不会用默认的加密设置
import requests
from requests.auth import HTTPBasicAuth
r=requests.get('xxx',auth=HTTPBasicAuth('user','password'))
print(r.status_code)

#HTTPBasicAuth可以简写为如下格式
import requests
r=requests.get('xxx',auth=('user','password'))
print(r.status_code)

异常处理

import requests
from requests.exceptions import * #可以查看requests.exceptions获取异常类型

try:
    r=requests.get('http://www.baidu.com',timeout=0.00001)
except ReadTimeout:
    print('===:')
# except ConnectionError: #网络不通
#     print('-----')
# except Timeout:
#     print('aaaaa')

except RequestException:
    print('Error')

实际上用一个exception Exception as e 捕获就行了

上传文件

请求中的files字段传入

import requests
files={'file':open('a.jpg','rb')}
respone=requests.post('http://httpbin.org/post',files=files)
print(respone.status_code)


解析库beautifulsoup

介绍

Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.你可能在寻找 Beautiful Soup3 的文档,Beautiful Soup 3 目前已经停止开发,官网推荐在现在的项目中使用Beautiful Soup 4, 移植到BS4

安装 Beautiful Soup

pip install beautifulsoup4

#安装解析器
Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是 lxml .根据操作系统不同,可以选择下列方法来安装lxml:

$ apt-get install Python-lxml

$ easy_install lxml

$ pip install lxml

另一个可供选择的解析器是纯Python实现的 html5lib , html5lib的解析方式与浏览器相同,可以选择下列方法来安装html5lib:

$ apt-get install Python-html5lib

$ easy_install html5lib

$ pip install html5lib

一般都选择使用lxml解析器

基本使用

传入html文本进行解析

如有如下html文本:

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p id="my p" class="title"><b id="bbb" class="boldest">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>
"""
#基本使用:容错处理,文档的容错能力指的是在html代码不完整的情况下,使用该模块可以识别该错误。使用BeautifulSoup解析上述代码,能够得到一个 BeautifulSoup 的对象,并能按照标准的缩进格式的结构输出
from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,'lxml') #传入html的文本,具有容错功能
res=soup.prettify() #处理好缩进,结构化显示
print(res)

获取标签的名称

p=soup.p.name # 对象.name 取到标签的名字

获取标签的属性

p=soup.p['class'] # class 是列表,可以有多个
name=soup.p['name']
attr=soup.p.attrs # 所有属性放到字典中

获取标签的内容

t=soup.p.text # 把p标签文本+子标签文本都拿出来
soup.p.string # p下的文本只有一个时,取到,否则为None
soup.p.strings #拿到一个生成器对象, 取到p下所有的文本内容
list(soup.p.strings)

嵌套选择

b=soup.body.p.b

子节点,子孙节点

soup.p.contents # p下所有子节点
soup.p.children # 得到一个迭代器,包含p下所有子节点
soup.p.descendants #获取子孙节点,p下所有的标签都会选择出来

父节点、祖先节点

soup.a.parent #获取a标签的父节点
soup.a.parents #找到a标签所有的祖先节点,父亲的父亲,父亲的父亲的父亲…挖祖坟

兄弟节点

soup.a.next_sibling #下一个兄弟
soup.a.previous_sibling #上一个兄弟


搜索文档树

五种过滤器

soup.find() :查找匹配成功的第一个标签对象源码中就是find_all()[0]
soup.find_all() :查找所有匹配成功的标签,返回列表

from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,'lxml')

#1、五种过滤器: 字符串、正则表达式、列表、True、方法
#1.1、字符串:即标签名
print(soup.find_all('b'))

#1.2、正则表达式
import re
print(soup.find_all(re.compile('^b'))) #找出b开头的标签,结果有body和b标签
print(soup.find_all(class_=re.compile('^s'),namd='a'))  # a标签并且class匹配正则的标签

#1.3、列表:如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有<a>标签和<b>标签:
print(soup.find_all(['a','b']))

#1.4、True:可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
print(soup.find_all(class_=True))  # 查找所有含有class的标签
print(soup.find_all(href=True))  # 查找所有含有href的标签
for tag in soup.find_all(True):
    print(tag.name)

#1.5、方法:如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False
def has_class_but_no_id(tag):
    return tag.has_attr('class') and not tag.has_attr('id')

print(soup.find_all(has_class_but_no_id))

find_all的各种参数( name , attrs , recursive , text , **kwargs )

#2.1 name: 搜索name参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是 True .
print(soup.find_all(name=re.compile('^t')))

#2.2、keyword: key=value的形式,value可以是过滤器:字符串 , 正则表达式 , 列表, True .
print(soup.find_all(id=re.compile('my')))
print(soup.find_all(href=re.compile('lacie'),id=re.compile('\d'))) #注意类要用class_
print(soup.find_all(id=True)) #查找有id属性的标签

#2.3、按照类名查找,注意关键字是class_,class_=value,value可以是五种选择器之一
print(soup.find_all('a',class_='sister')) #查找类为sister的a标签
print(soup.find_all('a',class_='sister ssss')) #查找类为sister和sss的a标签,顺序错误也匹配不成功
print(soup.find_all(class_=re.compile('^sis'))) #查找类为sister的所有标签

#2.4、attrs
print(soup.find_all('p',attrs={'class':'story'}))

# 有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性:
data_soup = BeautifulSoup('<div data-foo="value">foo!</div>','lxml')
# data_soup.find_all(data-foo="value") #报错:SyntaxError: keyword can't be an expression
# 但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag:
print(data_soup.find_all(attrs={"data-foo": "value"}))
# [<div data-foo="value">foo!</div>]

#2.5、text: 值可以是:字符,列表,True,正则
print(soup.find_all(text='Elsie'))
print(soup.find_all('a',text='Elsie'))

#2.6、limit参数:如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果,只返回limit条数的标签对象
print(soup.find_all('a',limit=2))

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


像调用 find_all() 一样调用tag
find_all() 几乎是Beautiful Soup中最常用的搜索方法,所以我们定义了它的简写方法. BeautifulSoup 对象和 tag 对象可以被当作一个方法来使用,这个方法的执行结果与调用这个对象的 find_all() 方法相同,下面两行代码是等价的:
soup.find_all("a")
soup("a")
这两行代码也是等价的:
soup.title.find_all(text=True)
soup.title(text=True)

CSS选择器

#该模块提供了select方法来支持css,详见官=网:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#id37

简单使用

from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,'lxml')

soup.p.select('.sister')
soup.select('.sister span')
soup.select('#link1')
soup.select('#link1 span')
soup.select('#list-2 .element.xxx')
soup.select('#list-2')[0].select('.element') #可以一直select,但其实没必要,一条select就可以了

2、获取属性

soup.select('#list-2 h1')[0].attrs

3、获取内容

soup.select('#list-2 h1')[0].get_text()

总结

  1. 推荐使用lxml解析库
  2. 讲了三种选择器:标签选择器,find与find_all,css选择器
    1. 标签选择器筛选功能弱,但是速度快
    2. 建议使用find,find_all查询匹配单个结果或者多个结果
    3. 如果对css选择器非常熟悉建议使用select
  3. 记住常用的获取属性attrs和文本值get_text()的方法

selenium

介绍

  1. 自动化测试工具,控制浏览器,模拟人的行为,做爬虫为了解决使用requests模块无法执行ajax获取数据,与无法直接执行JS代码(如滑动验证码)
  2. 使用selenium+半人工登录,获取cookie-----》给requests模块使用
  3. selenium本质是通过驱动浏览器,完全模拟浏览器的操作,比如跳转、输入、点击、下拉等,来拿到网页渲染之后的结果,可支持多种浏览器

安装

有界面浏览器

安装:selenium+chromedriver
pip3 install selenium

下载chromdriver.exe放到python安装路径的scripts目录中即可(或者在webdriver.Chrome(executable_path='chromedriver.exe')传入exe的路径),版本要与自己浏览器版本吻合
国内镜像网站地址: http://npm.taobao.org/mirrors/chromedriver/
最新的版本去官网找: https://sites.google.com/a/chromium.org/chromedriver/downloads

#注意:
selenium3默认支持的webdriverFirfox,而Firefox需要安装geckodriver
下载链接:https://github.com/mozilla/geckodriver/releases

无界面浏览器(基本用不上)

PhantomJS不再更新,看看即可

#安装:selenium+phantomjs
pip3 install selenium
下载phantomjs,解压后把phantomjs.exe所在的bin目录放到环境变量
下载链接:http://phantomjs.org/download.html

#验证安装
C:\Users\Administrator>phantomjs
phantomjs> console.log('egon gaga')
egon gaga
undefined
phantomjs> ^C
C:\Users\Administrator>python3
Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from selenium import webdriver
>>> driver=webdriver.PhantomJS() #无界面浏览器
>>> driver.get('https://www.baidu.com')
>>> driver.page_source

在 PhantomJS 年久失修, 后继无人的节骨眼
Chrome 出来救场, 再次成为了反爬虫 Team 的噩梦

自Google 发布 chrome 59 / 60 正式版 开始便支持Headless mode

这意味着在无 GUI 环境下, PhantomJS 不再是唯一选择

selenium+google的headless模式

#selenium:3.12.0
#webdriver:2.38
#chrome.exe: 65.0.3325.181(正式版本) (32 位)

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
chrome_options = Options()
chrome_options.add_argument('window-size=1920x3000') #指定浏览器分辨率
chrome_options.add_argument('--disable-gpu') #谷歌文档提到需要加上这个属性来规避bug
chrome_options.add_argument('--hide-scrollbars') #隐藏滚动条, 应对一些特殊页面
chrome_options.add_argument('blink-settings=imagesEnabled=false') #不加载图片, 提升速度
chrome_options.add_argument('--headless') #浏览器不提供可视化页面. linux下如果系统不支持可视化不加这条会启动失败
chrome_options.binary_location = r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" #手动指定使用的浏览器位置


driver=webdriver.Chrome(chrome_options=chrome_options)
driver.get('https://www.baidu.com')

print('hao123' in driver.page_source)

driver.close() #切记关闭浏览器,回收资源

基本使用

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By #按照什么方式查找,By.ID,By.CSS_SELECTOR
from selenium.webdriver.common.keys import Keys #键盘按键操作
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait #等待页面加载某些元素

browser=webdriver.Chrome(executable_path='chromedriver.exe')
try:
    browser.get('https://www.baidu.com')

input_tag=browser.find_element_by_id('kw')
input_tag.send_keys('美女') #python2中输入中文错误,字符串前加个u
input_tag.send_keys(Keys.ENTER) #输入回车

#隐式等待:在查找所有元素时,如果尚未被加载,则等10秒
browser.implicitly_wait(10)

print(browser.page_source)
print(browser.current_url)
print(browser.get_cookies())

finally:
    browser.close()

选择器

  1. find_element_by_id
  2. find_element_by_link_text
  3. find_element_by_partial_link_text
  4. find_element_by_tag_name
  5. find_element_by_class_name
  6. find_element_by_name
  7. find_element_by_css_selector
  8. find_element_by_xpath

强调:
1. 上述均可以改写成find_element(By.ID,'kw')的形式
2. find_elements_by_xxx的形式是查找到多个元素,结果为列表

xpath

拿lxml中的xpath方法举例

from lxml import etree

html=etree.HTML(doc)
# html=etree.parse('search.html',etree.HTMLParser())
1 所有节点
# a=html.xpath('//*')
2 指定节点(结果为列表)
# a=html.xpath('//head')
3 子节点,子孙节点
# a=html.xpath('//div/a')
# a=html.xpath('//body/a') #无数据,只能看儿子
# a=html.xpath('//body//a')
4 父节点
# a=html.xpath('//body//a[@href="image1.html"]/..')
# a=html.xpath('//body//a[1]/..')  # body下第一个a的爸爸
# 也可以这样
# a=html.xpath('//body//a[1]/parent::*')
5 属性匹配
# a=html.xpath('//body//a[@href="image1.html"]')

6 文本获取
# a=html.xpath('//body//a[@href="image1.html"]/text()')

7 属性获取
# a=html.xpath('//body//a/@href')
# # 注意从1 开始取(不是从0)
# a=html.xpath('//body//a[1]/@href')  # 第一个a的网址
8 属性多值匹配
#  a 标签有多个class类,直接匹配就不可以了,需要用contains
# a=html.xpath('//body//a[@class="li"]')
# a=html.xpath('//body//a[contains(@class,"li")]')
# a=html.xpath('//body//a[contains(@class,"li")]/text()')
9 多属性匹配
# a=html.xpath('//body//a[contains(@class,"li") or @name="items"]')
# a=html.xpath('//body//a[contains(@class,"li") and @name="items"]/text()')
# # a=html.xpath('//body//a[contains(@class,"li")]/text()')
10 按序选择
# a=html.xpath('//a[2]/text()')
# a=html.xpath('//a[2]/@href')
取最后一个last()
# a=html.xpath('//a[last()]/@href')
位置小于3的position()<3
# a=html.xpath('//a[position()<3]/@href')
倒数第二个last()-2
# a=html.xpath('//a[last()-2]/@href')
11 节点轴选择
ancestor:祖先节点
# 使用了* 获取所有祖先节点
# a=html.xpath('//a/ancestor::*')
# # 获取祖先节点中的div
# a=html.xpath('//a/ancestor::div')
attribute:属性值
# a=html.xpath('//a[1]/attribute::*')
child:直接子节点
# a=html.xpath('//a[1]/child::*')
descendant:所有子孙节点
# a=html.xpath('//a[6]/descendant::*')
following:当前节点之后所有节点
# a=html.xpath('//a[1]/following::*')
# a=html.xpath('//a[1]/following::*[1]/@href')
following-sibling:当前节点之后同级节点
# a=html.xpath('//a[1]/following-sibling::*')
# a=html.xpath('//a[1]/following-sibling::a')
# a=html.xpath('//a[1]/following-sibling::*[2]')
# a=html.xpath('//a[1]/following-sibling::*[2]/@href')

获取标签对象各种属性

 browser=webdriver.Chrome()

browser.get('https://www.amazon.cn/')

wait=WebDriverWait(browser,10)
wait.until(EC.presence_of_element_located((By.ID,'cc-lm-tcgShowImgContainer')))

tag=browser.find_element(By.CSS_SELECTOR,'#cc-lm-tcgShowImgContainer img')

#获取标签属性,
print(tag.get_attribute('src'))

#获取标签ID,位置,名称,大小(了解)
print(tag.id)
print(tag.location)
print(tag.tag_name)
print(tag.size)
print(tag.text)

# 标签对象可以继续查找下去
tag_a = tag.find_element_by_xpath('./a[1]')  # 如xpath需要 . 开头,代表从当前位置继续

browser.close()

等待元素被加载

  1. selenium只是模拟浏览器的行为,而浏览器解析页面是需要时间的(执行css,js),一些元素可能需要过一段时间才能加载出来,为了保证能查找到元素,必须等待

  2. 等待的方式分两种:
    隐式等待:在browser.get('xxx')前就设置,针对所有元素有效
    显式等待:在browser.get('xxx')之后设置,只针对某个元素有效

隐式等待示例:

browser=webdriver.Chrome()
browser.get('https://www.baidu.com')

#隐式等待:在查找所有元素时,如果尚未被加载,则等10秒
browser.implicitly_wait(10)

input_tag=browser.find_element_by_id('kw')
input_tag.send_keys('美女')
input_tag.send_keys(Keys.ENTER)

显式等待示例:

from selenium.webdriver.support.wait import WebDriverWait #等待页面加载某些元素
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By #按照什么方式查找,By.ID,By.CSS_SELECTOR

#显式等待:显式地等待10s某个元素被加载
wait=WebDriverWait(browser,10)
wait.until(EC.presence_of_element_located((By.ID,'content_left')))

元素交互操作

点击,清空

input_tag.send_keys('iphone 8')  # 输入
button.click()  # 点击
input_tag.clear()  # 清空

动作链

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By  # 按照什么方式查找,By.ID,By.CSS_SELECTOR
from selenium.webdriver.common.keys import Keys  # 键盘按键操作
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait  # 等待页面加载某些元素
import time

driver = webdriver.Chrome()
driver.get('http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
wait=WebDriverWait(driver,3)
# driver.implicitly_wait(3)  # 使用隐式等待

try:
    driver.switch_to.frame('iframeResult') ##切换html到iframeResult
    sourse=driver.find_element_by_id('draggable')
    target=driver.find_element_by_id('droppable')
#方式一:基于同一个动作链串行执行
# actions=ActionChains(driver) #拿到动作链对象
# actions.drag_and_drop(sourse,target) #把动作放到动作链中,准备串行执行,将sourse拖动到target
# actions.perform()  # 执行
#方式二:不同的动作链,每次移动的位移都不同
# 基于坐标进行位移
ActionChains(driver).click_and_hold(sourse).perform()  # 鼠标左键按住滑块
distance=target.location['x']-sourse.location['x']  # 需要向右位移的距离

track=0
while track < distance:
    ActionChains(driver).move_by_offset(xoffset=2,yoffset=0).perform()  # 
    track+=2

ActionChains(driver).release().perform()  # 松开鼠标左键

time.sleep(10)

finally:
    driver.close()

执行JS

browser.execute_script('alert("hello world")') #打印警告

补充:frame切换

frame相当于一个单独的网页,在父frame里是无法直接查看到子frame的元素的,必须switch_to_frame切到该frame下,才能进一步查找

browser.switch_to.frame('iframeResult') #切换到id为iframeResult的frame

之后browser就是这个frame下的对象了,后续再查找也是在这个frame中查找

其他

模拟浏览器的前进后退
browser.back() # 退

browser.forward() # 进
cookies
coo = browser.get_cookies()  # 取得cookie字典

for i in coo:
	browser.add_cookie(i)  # 将cookie存入
选项卡管理
browser.execute_script('window.open()')  # 执行js:打开新的选项卡

browser.window_handles  # 获取所有的选项卡

browser.switch_to_window(browser.window_handles[1])  # 选择选项卡,这里选的是第二个,从0开始算

browser.switch_to_window(browser.window_handles[0])  # 选择第一个选项卡
异常处理
from selenium.common.exceptions import TimeoutException,NoSuchElementException,NoSuchFrameException

try:
    browser=webdriver.Chrome()
    browser.get('http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
    browser.switch_to.frame('iframssseResult')

except TimeoutException as e:
    print(e)
except NoSuchFrameException as e:
    print(e)
finally:
    browser.close()

老样子这里跟requests一样,用Exception as e就完事了其实,若需要细粒度的可以使用上面的方法

项目练习

爬拉钩网职位信息

#https://www.lagou.com/jobs/positionAjax.json?city=%E4%B8%8A%E6%B5%B7&needAddtionalResult=false
import requests
#实际要爬取的url
url = 'https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false'

payload = {
    'first': 'true',
    'pn': '1',
    'kd': 'python',
}

header = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
    'Referer': 'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput=',
    'Accept': 'application/json, text/javascript, */*; q=0.01'
}
#原始的url
urls ='https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput='
#建立session
s = requests.Session()
# 获取搜索页的cookies
s.get(urls, headers=header, timeout=3)
# 为此次获取的cookies
cookie = s.cookies
# 获取此次文本
response = s.post(url, data=payload, headers=header, cookies=cookie, timeout=5).text
print(response)

爬取红楼梦小说

#http://www.shicimingju.com/book/hongloumeng.html

import requests

from bs4 import BeautifulSoup
ret=requests.get('https://www.shicimingju.com/book/hongloumeng.html')
# print(ret.text)

soup=BeautifulSoup(ret.text,'lxml')
li_list=soup.find(class_='book-mulu').find('ul').find_all('li')
with open('hlm.txt','w',encoding='utf-8') as f:
    for li in li_list:
        content=li.find('a').text
        url='https://www.shicimingju.com'+li.find('a').get('href')

        f.write(content)
        f.write('\n')
        res_content=requests.get(url)
        soup2=BeautifulSoup(res_content.text,'lxml')
        content_detail=soup2.find(class_='chapter_content').text
        f.write(content_detail)
        f.write('\n')
        print(content,'写入了')

爬取肯德基门店

# http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword
import requests

header = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'
}
data = {
    'cname': '',
    'pid': 20,
    'keyword': '浦东',
    'pageIndex': 1,
    'pageSize': 10
}
ret = requests.post('http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword', data=data, headers=header)
print(ret.json())

爬取糗事百科

#https://www.qiushibaike.com/text/page/2/
import requests
from bs4 import BeautifulSoup
ret=requests.get('https://www.qiushibaike.com/text/page/2/')
# print(ret.text)

soup=BeautifulSoup(ret.text,'html.parser')

article_list=soup.find_all(class_='article')
# print(article_list)
for article in article_list:
    content=article.find(class_='content').text
    print(content)
    print('-------')

爬取chouti网并自动点赞

首先使用selenium获取登录后的cookie

from  selenium import webdriver
import time
import json
bro=webdriver.Chrome(executable_path='chromedriver.exe')

bro.get('https://dig.chouti.com/')
bro.maximize_window()  # 最大化
bro.implicitly_wait(10)
login_btn=bro.find_element_by_link_text('登录')
login_btn.click()
name_input=bro.find_element_by_name('phone')
password_input=bro.find_element_by_name('password')
name_input.send_keys('抽屉账号')
password_input.send_keys('抽屉密码')

login_real_btn=bro.find_element_by_css_selector('button.login-btn')
login_real_btn.click()

# 可能有验证码,手动搞定
time.sleep(20)

cookie=bro.get_cookies()

print(cookie)
with open('cookie.json','w') as f:
    json.dump(cookie,f)
print('cookie写入到文件中了')

使用cookiejar用requests搞定


# 自动点赞功能
import requests
from requests.cookies import RequestsCookieJar
header = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36',
    'Referer': 'https://dig.chouti.com/',
}

res = requests.get('https://dig.chouti.com/link/hot', headers=header)
print(res.json())
jar=RequestsCookieJar()
# 从文件中读出来,写到cookie中
with open('cookie.json','r') as f:
    cookie_l=json.load(f)
    for cookie in cookie_l:
        jar.set(cookie['name'], cookie['value'])

for item in res.json()['data']:
    id = item['id']
    print(id)
    
    # 点赞请求,也不能缺cookie
    data={
        'linkId':id
    }
    res = requests.post('https://dig.chouti.com/link/vote', headers=header,cookies=jar,data=data)
    # res = requests.post('https://dig.chouti.com/link/vote', headers=header,data=data)
    print(res.text)

登录1***6

from selenium import webdriver
import time
#pillow
from PIL import Image

# 引入超级鹰
from chaojiying import Chaojiying_Client

from selenium.webdriver import ActionChains

bro=webdriver.Chrome(executable_path='./chromedriver.exe')
bro.implicitly_wait(10)
try:
    bro.get('https://kyfw.12306.cn/otn/resources/login.html')
    bro.maximize_window()  # 窗口最大化,全屏
    button_z=bro.find_element_by_css_selector('.login-hd-account a')
    button_z.click()
    time.sleep(2)
    # 截取整个屏幕
    bro.save_screenshot('./main.png')
    # 验证码的位置和大小
    img_t=bro.find_element_by_id('J-loginImg')
    print(img_t.size)
    print(img_t.location)

    size=img_t.size
    location=img_t.location

    img_tu = (int(location['x']), int(location['y']), int(location['x'] + size['width']), int(location['y'] + size['height']))
    # # 抠出验证码
    # #打开
    img = Image.open('./main.png')
    # 抠图
    fram = img.crop(img_tu)
    # 截出来的小图
    fram.save('code.png')

    # 调用超级鹰破解
    chaojiying = Chaojiying_Client('超级鹰账号', '超级鹰密码', '903641')	#用户中心>>软件ID 生成一个替换 96001,最后这个是破解模板的ID
    im = open('code.png', 'rb').read()													#本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
    # print(chaojiying.PostPic(im, 9004))

    ## 返回结果如果有多个 260,133|123,233,处理这种格式[[260,133],[123,233]]
    res=chaojiying.PostPic(im, 9004)
    print(res)
    result=res['pic_str']

    all_list = []
    if '|' in result:
        list_1 = result.split('|')
        count_1 = len(list_1)
        for i in range(count_1):
            xy_list = []
            x = int(list_1[i].split(',')[0])
            y = int(list_1[i].split(',')[1])
            xy_list.append(x)
            xy_list.append(y)
            all_list.append(xy_list)
    else:
        x = int(result.split(',')[0])
        y = int(result.split(',')[1])
        xy_list = []
        xy_list.append(x)
        xy_list.append(y)
        all_list.append(xy_list)
    print(all_list)
    # 用动作链,点击图片
    # [[260,133],[123,233]]
    for a in all_list:
        x = a[0]
        y = a[1]
        ActionChains(bro).move_to_element_with_offset(img_t, x, y).click().perform()
        time.sleep(1)

    username=bro.find_element_by_id('J-userName')
    username.send_keys('12306账号')
    password=bro.find_element_by_id('J-password')
    password.send_keys('12306密码')
    time.sleep(3)
    submit_login=bro.find_element_by_id('J-login')
    submit_login.click()
    time.sleep(3)

    print(bro.get_cookies())
    time.sleep(10)
    bro.get('https://www.12306.cn/index/')
    time.sleep(5)

except Exception as e:
    print(e)
finally:
    bro.close()

selenium爬取京东商品信息

from selenium import webdriver
from selenium.webdriver.common.keys import Keys


# 定义爬取函数
def get_goods(bro):
    # 滑倒屏幕底部
    bro.execute_script('scroll(0,document.body.scrollHeight)')
    li_list = bro.find_elements_by_class_name('gl-item')
    for li in li_list:
        try:
            img_url = li.find_element_by_css_selector('.p-img>a>img').get_attribute('src')
            if not img_url:
                img_url = li.find_element_by_css_selector('.p-img>a>img').get_attribute('data-lazy-img')
            img_url = 'https:' + img_url

            name = li.find_element_by_css_selector('.p-name em').text
            url = li.find_element_by_css_selector('.p-img>a').get_attribute('href')
            price = li.find_element_by_css_selector('.p-price i').text
            commit = li.find_element_by_css_selector('.p-commit a').text
            print('''
            商品名称:%s
            商品价格:%s
            商品链接:%s
            图片链接:%s
            商品评论数:%s
    
            ''' % (name, price, url, img_url, commit))
        except Exception as e:
            continue

    # 点击下一页

    next = bro.find_element_by_partial_link_text('下一页')
    next.click()
    # 再解析下一页的内容
    get_goods(bro)

bro = webdriver.Chrome(executable_path='chromedriver.exe')

try:
    bro.get('https://www.jd.com/')
    # 隐式等待

    bro.implicitly_wait(10)
    search_in = bro.find_element_by_id('key')
    search_in.send_keys('男士内衣')

    search_in.send_keys(Keys.ENTER)  # 敲击回车

    # bro.page_source--->bs4--lxml
    get_goods(bro)
except Exception as e:
    print(e)

finally:
    bro.close()
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值