2020-04-01

爬虫基础

爬虫认知

是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。通俗的讲: 就是模拟客户端发起网络请求,接收请求的响应,按照一定的规则,自动的抓取互联网信息的程序。

网络请求

urllib库

导入库

from urllib import request
from urllib import parse

urlopen()函数

"""
urlopen()函数:在urllib.request模块中,用来网络请求
参数:   1.url:请求网站的url  2.data:请求的data提交的表单数据  
返回值: 返回值是一个 http.client.HTTPResponse 对象,这个对象是一个类文件句柄对象。
        有 read(size) 、 readline 、 readlines 以及 getcode 等方法。
"""
# respone = request.urlopen('http://www.baidu.com')
# print(respone.getcode())

urlretrieve() 函数

"""
urlretrieve() 函数的用法: 将网页上的文件保存到本地
参数:1.文件地址 2.要保存的文件名 + 后缀名
"""
# img_url = 'https://edu-image.nosdn.127.net/deaf24ba-2bd5-4266-8c65-a9a615fef61e.jpg?imageView&quality=100&crop=0_0_1080_680&thumbnail=450y250'
# request.urlretrieve(img_url,'21天搞定Python分布爬虫.png')

urlencode()函数

"""
urlencode()函数用法: 发送请求时候,如果url中包含中文或者其他特殊字符,则需要进行路径编码
                    urlencode 可以把字典塑胶转换成URL编码的数据
"""
# params = {'name':'张三',"age":18,'greet':'hello world'}
# result = parse.urlencode(params)
# print(result)

# url = 'http://www.baidu.com/s'
# params = {"wd":"刘德华"}
# qs = parse.urlencode(params)
# url = url + "?" + qs
# resp = request.urlopen(url)
# print(resp.read())

parse_qs()函数

"""
parse_qs()函数用法: 讲经过编码后的url参数进行解码
"""
# params = {'name':'张三',"age":18,'greet':'hello world'}
# qs = parse.urlencode(params) # 编码
# print(qs)
# restult = parse.parse_qs(qs) # 解码
# print(restult)
#

urlparse() 和 urlsplit()函数

"""
urlparse() 和 urlsplit()函数: 将URL中各个组成部分进行分割
区别:     urlparse 里面多了一个 params 属性,而 urlsplit 没有这个 params 属性。
"""
# url = 'http://www.baidu.com/s?username=zhiliao'
#
# result =  parse.urlsplit(url)
# result2 = parse.urlparse(url)
#
# print('urlsplit: ',result)
# print('urlparse: ',result2)
#
# print('scheme: ',result.scheme)
# print('netloc ',result.netloc)
# print('path ',result.path)
# print('query ',result.query)

request.Request类

"""
request.Request类:
    # 如果要在请求时候添加某些 请求信息(如:headers、data等 网络请求必须要的参数),就需要 Request类进行封装请求数据
    # 类型转换: str => bytes:encode() ;   bytes => str:decode()
    # Request:默认情况是 get()请求方法,但是只要传了 data参数,就是post()请求方法
"""
# from urllib import request,parse
#
# url = "https://music.163.com/weapi/cloudsearch/get/web?csrf_token="
# headers = {
#     "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36"
# }
# formdata = {
#     "params": "foUlX36kSAGPWtUp5SoqQTK9qDpJUuld45H57CYE49VM4zPJq8qwZxFwvL5djdWf61EFKPmFBZEzQ0wajuz1dE8a/pPKLciXET2mpGG3Pq1cWCMkOL1/kOSre2Qu0dHojZjHbHii/RbSNtmdIwVKbRD660wt8FBRRamrKbryU9NriJv5bLagmaFQ7aaOh5QQ6fjS2ENsAViwTVU58yvD85TWjS6zop28bhbLGePML4x3n8qaYIlAza4UNKaTAJ3wZNn7AdVP2FGbzD8NiSYjW/Wc+UwgsqWjsUU5wzmFf/Q=",
#     "encSecKey": "32c74c08ae72ff1e5c39de0fb64b15b981d50f873a0afb73c4511939d67bc83fd101ed53f347798a10a86fb46cb55f651c1256413de44cd9ecc197c5cbd3eda58aa4075aeb2ca73b0fe4c6b23248200dc85b23f3206ab06e6e4568aa02219396b8203f886dc3fa08c2a7e2ff283eabf60e992bc10f7bafb9ab89ab8dd3b3a020"
# }
#
# req = request.Request(url=url,headers=headers,data=parse.urlencode(formdata).encode('utf-8'))
# resp = request.urlopen(req)
# print(resp.read().decode('utf-8'))

# from urllib import request
#
# headers = {
#     "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
#     "Referer": "https://www.qiushibaike.com/text/",
#     "Origin": "https://www.qiushibaike.com"
# }
# req = request.Request("https://www.qiushibaike.com/text/page/2/",headers=headers)
# resp = request.urlopen(req)
# print(resp.read().decode('utf-8'))

ProxyHandler()代理处理器

"""
ProxyHandler处理器(代理设置):
访问网站频繁被识别为 爬虫:  网站会检测某一段时间某个IP的访问次数,如果访问次数多的不像正常人,它会禁止这个IP的访问。
可以通过IP代理服务器 进行 反爬; 自定义 opener()方法  来使用代理
"""
# from urllib import request
#
# ## 没使用 IP代理情况
# # url = 'http://httpbin.org/ip'
# # resp = request.urlopen(url)
# # print(resp.read())

# ## 使用IP代理
# url = 'http://httpbin.org/ip'
# # 1. 使用ProxyHandler()方法,传入代理传教一个 handler
# handler = request.ProxyHandler({"118.113.245.100":"9999"}) # ProxyHandler()传入一个字典:{"ip":"prot"}
# # 2.使用上面创建的handler 构建一个 opener 代理对象
# opener = request.build_opener(handler)
# # 3.使用 opener代理对象 发送请求
# resp = opener.open(url)
# print(resp.read())

ProxyHandler处理器 总结

"""
    ### ProxyHandler处理器(代理):
    1. 代理的原理:在请求目的网站之前,先请求代理服务器,然后让代理服务器去请求目的网站,
       代理服务器拿到目的网站的数据后,再转发给我们的代码。
    2. http://httpbin.org:这个网站可以方便的查看http请求的一些参数。
    3. 在代码中使用代理:
        * 使用`urllib.request.ProxyHandler`,传入一个代理,这个代理是一个字典,
          字典的key依赖于代理服务器能够接收的类型,一般是`http`或者`https`,值是`ip:port`。
        * 使用上一步创建的`handler`,以及`request.build_opener`创建一个`opener`对象。
        * 使用上一步创建的`opener`,调用`open`函数,发起请求。
    示例代码如下:
    ```python
    from urllib import request
    url = 'http://httpbin.org/ip'
    # 1. 使用ProxyHandler,传入代理构建一个handler
    handler = request.ProxyHandler({"http":"223.241.78.43:8010"})
    # 2. 使用上面创建的handler构建一个opener
    opener = request.build_opener(handler)
    # 3. 使用opener去发送一个请求
    resp = opener.open(url)
    print(resp.read())
    ```
"""

Cookie技术

"""
网络连接中,http协议是无状态的、没记忆:服务器与 客服端 无法进行识别;从而无法可持续进行数据传输、交互;
Cookie技术 :则是解决http传输协议的缺点,保存 服务器和客户端 连接后的认证信息,让两者后续 进行有效的 连接、访问、交流
"""
""" 保存 cookie 到本地,可以使用 cookiejar 的 save 方法,并且需要指定一个文件名: """
# from urllib import request
# from http.cookiejar import MozillaCookieJar
#
# headers = {
# 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/62.0.3202.94 Safari/537.36'
# }
#
# cookiejar = MozillaCookieJar("cookie.txt") # 生成 存储 cookie的文件
# handler = request.HTTPCookieProcessor(cookiejar)
# opener = request.build_opener(handler)
#
# req = request.Request('http://httpbin.org/cookies',headers=headers)
# resp = opener.open(req)
#
# print(resp.read())
# cookiejar.save(ignore_discard=True,ignore_expires=True) # 保存 cookie
"""从本地加载 cookie ,需要使用 cookiejar 的 load 方法,并且也需要指定方法:"""
# from urllib import request
# from http.cookiejar import MozillaCookieJar
#
# headers = {
# 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/62.0.3202.94 Safari/537.36'
# }
#
# cookiejar = MozillaCookieJar('cookie.txt')
# cookiejar.load(ignore_discard=True)   # 加载 cookie文件 信息
# handler = request.HTTPCookieProcessor(cookiejar)
# opener = request.build_opener(handler)
#
# resp = opener.open('http://httpbin.org/cookies')
# for cookie in cookiejar:
#     print(cookie)
# 大鹏董成鹏主页:http://www.renren.com/880151247/profile
# 人人网登录url:http://www.renren.com/PLogin.do

"""1. 不使用cookie去请求大鹏的主页  => 结果:请求失败,需要登录验证;"""
from urllib import request

# dapeng_url = "http://www.renren.com/880151247/profile"
# headers = {
#     'User-Agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
#     "Cookie":"anonymid=jacdwz2x-8bjldx; depovince=GW; _r01_=1; _ga=GA1.2.1455063316.1511436360; _gid=GA1.2.862627163.1511436360; wp=1; JSESSIONID=abcrs0queAwRp_sk9dS-v; ch_id=10016; jebecookies=c68b9b55-b6a1-4661-8d11-832862cfa246|||||; ick_login=7e3299f4-1e31-4455-b2a4-e5317c9e2ccf; _de=EA5778F44555C091303554EBBEB4676C696BF75400CE19CC; p=a96969b7912d80d95127b7935c1b729e1; first_login_flag=1; [email protected]; ln_hurl=http://hdn.xnimg.cn/photos/hdn121/20170428/1700/main_nhiB_aebd0000854a1986.jpg; t=4d2ccb81ee83a6b1d3925b94779d22e21; societyguester=4d2ccb81ee83a6b1d3925b94779d22e21; id=443362311; xnsid=13bf03ea; loginfrom=syshome; jebe_key=9c062f5a-4335-4a91-bf7a-970f8b86a64e%7Ca022c303305d1b2ab6b5089643e4b5de%7C1511449232839%7C1; wp_fold=0"
# }
# req = request.Request(url=dapeng_url,headers=headers)
# resp = request.urlopen(req)
# with open('renren.html','w',encoding='utf-8') as fp:
#     # write函数必须写入一个str的数据类型
#     # resp.read()读出来的是一个bytes数据类型
#     # bytes -> decode -> str
#     # str -> encode -> bytes
#     fp.write(resp.read().decode('utf-8'))
# 大鹏董成鹏主页:http://www.renren.com/880151247/profile
# 人人网登录url:http://www.renren.com/PLogin.do

"""2. 使用cookie去请求大鹏的主页  => 结果:请求成功,返回对应信息"""
from urllib import request
from urllib import  parse
from http.cookiejar import CookieJar

headers = {
    'User-Agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"
}

# 创建 共享的请求对象,保存验证信息
def get_opener():
    cookiejar = CookieJar() # 创建 cookiejar 对象
    handler = request.HTTPCookieProcessor(cookiejar) # 创建一个 HTTPCookieProcessor() HttpCookie处理器对象
    opener = request.build_opener(handler)  # 创建一个 携带cookie信息的 opener()请求对象
    return opener

# 登录,并获取登录信息
def login_renren(opener):
    # 1.使用opener发送登录的请求(人人网的邮箱和密码)
    data = {
        'email':"[email protected]",
        'password': "pythonspider"
    }
    login_url = "http://www.renren.com/PLogin.do"
    req = request.Request(login_url,data=parse.urlencode(data).encode('utf-8'),headers=headers)
    response = opener.open(req)
    print(response.read().decode("utf-8"))

# 使用已登录信息,进行访问主页
def visit_profile(opener):
    # 2. 访问个人主页
    dapeng_url = "http://www.renren.com/880151247/profile"
    # 获取个人主页的页面的时候,不要新建一个opener;而应该使用之前的那个opener,
    # 因为之前的那个opener已经包含了登录所需要的cookie信息
    req = request.Request(dapeng_url,headers=headers)
    resp = opener.open(req)
    with open('abc.html','w',encoding='utf-8') as fp:
        fp.write(resp.read().decode('utf-8'))


if __name__ == '__main__':
    opener = get_opener()
    login_renren(opener)
    visit_profile(opener)

requests库

requests库

"""
requests库:
    虽然Python的标准库中 urllib模块已经包含了平常我们使用的大多数功能,但是它的 API 使用起来
    让人感觉不太好,而 Requests宣传是 “HTTP for Humans”,说明使用更简洁方便。
"""
# import requests
#
# # 最简单的发送 get 请求就是通过 requests.get 来调用
# response = requests.get("https://www.baidu.com/") # params 接收一个字典或者字符串的查询参数,字典类型自动转换为url编码,不需要urlencode()
# print(type(response.text)) #
# print(response.text) #查看响应内容,response.text 返回的是Unicode格式的数据
# print(type(response.content)) #
# print(response.content) # 查看响应内容,response.content返回的字节流数据
# print(response.content.decode('utf-8')) #
#
# print(response.url) # # 查看完整url地址
# print(response.encoding) #查看响应头部字符编码
# print(response.status_code) # 查看响应码

发送GET请求

## 1. params 传入 参数形式:

# import requests
#
# params = {
#     'wd': '中国'
# }
# headers = {
#     'User-Agent': 'ozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36'
# }
# response = requests.get("https://www.baidu.com/s",params=params,headers=headers)
#
# with open('baidu.html','w',encoding='utf-8') as fp:
#     fp.write(response.content.decode('utf-8'))
#
# print(response.url)

发送POST请求

## 2. data 传入 表单数据 形式:
# import requests
#
# url = 'https://www.lagou.com/jobs/positionAjax.json?city=%E6%B7%B1%E5%9C%B3&needAddtionalResult=false&isSchoolJob=0'
#
# data = {
#     'first':"true",
#     'pn': '1',
#     'kd': 'python'
# }
# headers = {
#     'Referer': 'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput=',
#     'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36'
# }
#
# response = requests.post(url,data=data,headers=headers)
# print(type(response.json()))
# print(response.json())

proxies代理

## 3. proxies 传入 代理服务器 形式:
# import requests
#
# proxy = {
#     'http': '182.148.206.83:9999'
# }
#
# response = requests.get("http://httpbin.org/ip",proxies=proxy)
# print(response.text)

verify 处理不信任

## 4. verify 处理不信任的SSL证书 形式:
# import requests
#
# url = 'http://www.12306.cn/mormhweb/'
# resp = requests.get(url,verify=True)
# with open('12306.html','w',encoding='utf-8') as fp:
#     fp.write(resp.text)

cookie

## 5. cookie:如果在一个响应中包含了 cookie ,返回的 cookie 值:  形式:
# import requests
# url = "http://www.renren.com/PLogin.do"
# data = {"email":"[email protected]",'password':"pythonspider"}
# resp = requests.get('http://www.baidu.com/')
# print(resp.cookies)
# print(resp.cookies.get_dict())

session

## 6. session: 保存会话信息并 共享 cookie数据  形式:
import requests

# response = requests.get('https://www.baidu.com/')
# print(response.cookies.get_dict())

# url = "http://www.renren.com/PLogin.do"
# data = {"email":"[email protected]",'password':"pythonspider"}
# headers = {
#     'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"
# }
#
# session = requests.Session()
#
# session.post(url,data=data,headers=headers) # 先提交数据,通过验证
#
# response = session.get('http://www.renren.com/880151247/profile') # 再访问主页
# with open('renren.html','w',encoding='utf-8') as fp:
#     fp.write(response.text)

request 和 session 小案例

import requests

def renren():
    url = "http://www.renren.com/PLogin.do"
    data = {"email": "[email protected]", 'password': "pythonspider"}
    headers = {
        'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"
    }

    # 登录
    session = requests.session()
    session.post(url, data=data, headers=headers)

    # 访问大鹏个人中心
    resp = session.get('http://www.renren.com/880151247/profile')

    print(resp.text)

requests笔记

## 发送get请求:
发送get请求,直接调用`requests.get`就可以了。想要发送什么类型的请求,就调用什么方法。
​```python
response = requests.get("https://www.baidu.com/")
​```

## response的一些属性:
​```python
import requests

kw = {'wd':'中国'}

headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}

# params 接收一个字典或者字符串的查询参数,字典类型自动转换为url编码,不需要urlencode()
response = requests.get("http://www.baidu.com/s", params = kw, headers = headers)

# 查看响应内容,response.text 返回的是Unicode格式的数据
print(response.text)

# 查看响应内容,response.content返回的字节流数据
print(response.content)

# 查看完整url地址
print(response.url)

# 查看响应头部字符编码
print(response.encoding)

# 查看响应码
print(response.status_code)
​```

## response.text和response.content的区别:
1. response.content:这个是直接从网络上面抓取的数据。没有经过任何解码。所以是一个bytes类型。其实在硬盘上和在网络上传输的字符串都是bytes类型。
2. response.text:这个是str的数据类型,是requests库将response.content进行解码的字符串。解码需要指定一个编码方式,requests会根据自己的猜测来判断编码的方式。所以有时候可能会猜测错误,就会导致解码产生乱码。这时候就应该使用`response.content.decode('utf-8')`进行手动解码。

## 发送post请求:
发送post请求非常简单。直接调用`requests.post`方法就可以了。
如果返回的是json数据。那么可以调用`response.json()`来将json字符串转换为字典或者列表。

## 使用代理:
在请求方法中,传递`proxies`参数就可以了。

## 处理cookie:
如果想要在多次请求中共享cookie。那么应该使用session。示例代码如下:
​```python
import requests
url = "http://www.renren.com/PLogin.do"
data = {"email":"[email protected]",'password':"pythonspider"}
headers = {
    'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"
}

session = requests.Session()

session.post(url,data=data,headers=headers)

response = session.get('http://www.renren.com/880151247/profile')
with open('renren.html','w',encoding='utf-8') as fp:
    fp.write(response.text)
​```

## 处理没有授权的https协议:

数据解析

xpath库

path语法

## 使用方式:
使用//获取整个页面当中的元素,然后写标签名,然后再写谓词进行提取。比如:
​```
//div[@class='abc']
​```

## 需要注意的知识点:
1. /和//的区别:/代表只获取直接子节点。//获取子孙节点。一般//用得比较多。当然也要视情况而定。
2. contains:有时候某个属性中包含了多个值,那么可以使用`contains`函数。示例代码如下:
    ```
    //div[contains(@class,'job_detail')]
    ```
3. 谓词中的下标是从1开始的,不是从0开始的。

## 使用lxml解析HTML代码:
1. 解析html字符串:使用`lxml.etree.HTML`进行解析。示例代码如下:
    ```python
    htmlElement = etree.HTML(text)
    print(etree.tostring(htmlElement,encoding='utf-8').decode("utf-8"))
    ```
2. 解析html文件:使用`lxml.etree.parse`进行解析。示例代码如下:
    ```python
    htmlElement = etree.parse("tencent.html")
    print(etree.tostring(htmlElement, encoding='utf-8').decode('utf-8'))
    ```
    这个函数默认使用的是`XML`解析器,所以如果碰到一些不规范的`HTML`代码的时候就会解析错误,这时候就要自己创建`HTML`解析器。
    ```python
    parser = etree.HTMLParser(encoding='utf-8')
    htmlElement = etree.parse("lagou.html",parser=parser)
    print(etree.tostring(htmlElement, encoding='utf-8').decode('utf-8'))
    ```

    ## lxml结合xpath注意事项:
    1. 使用`xpath`语法。应该使用`Element.xpath`方法。来执行xpath的选择。示例代码如下:
        ```python
        trs = html.xpath("//tr[position()>1]")
        ```
    `xpath函数`返回来的永远是一个列表。
    2. 获取某个标签的属性:
        ```python
        href = html.xpath("//a/@href")
        # 获取a标签的href属性对应的值
        ```
    3. 获取文本,是通过`xpath`中的`text()`函数。示例代码如下:
        ```python
        address = tr.xpath("./td[4]/text()")[0]
        ```
        
    4. 指定获取元素 下标范围,是通过`xpath`中的`position()`函数。示例代码如下:
        ```python
        trs = html.xpath("//tr[position()>1]")
        ```      
        
    5. 在某个标签下,再执行xpath函数,获取这个标签下的子孙元素,那么应该在斜杠之前加一个点,代表是在当前元素下获取。示例代码如下:
        ```python
         address = tr.xpath("./td[4]/text()")[0]
        ```

导入库

from lxml import etree

解析 HTML文件 和文本

class ParseFile():

    def parse_text(self):
        text = """html文本"""

        # 调用 HTML()方法 来加载 html文本,并结构化
        htmlElement = etree.HTML(text)
        print(etree.tostring(htmlElement,encoding='utf8').decode('utf-8') )


    def parse_tencent_file(self):
        # 调用 XML模块中 etree类的parse方法,能导入文件并 解析
        htmlElement = etree.parse("htmlElement.html")
        print(etree.tostring(htmlElement, encoding='utf-8').decode('utf-8'))


    def parse_lagou_file(self):
        # 指定 HTMLParser解析器
        # 调用 XML模块中 etree类的parse方法,能导入文件并 解析
        parser = etree.HTMLParser(encoding='utf8')
        htmlElement = etree.parse('lagou.html',parser=parser)
        print(etree.tostring(htmlElement,encoding='utf-8').decode('utf-8'))

使用xpath 提取 元素

class ExtractElement(object):

    parser = etree.HTMLParser(encoding='utf8')
    htmlElement = etree.parse("tencent.html", parser=parser)

    # 1. 获取所有tr标签
    # trs = htmlElement.xpath('//tr')
    # for tr in trs:
    #     print(etree.tostring(tr,encoding='utf8').decode('utf-8'))

    # 2. 获取第2个tr标签
    # tr = htmlElement.xpath('//tr[2]')[0]
    # print(etree.tostring(tr,encoding='utf-8').decode('utf-8'))

    # 3. 获取所有class等于even的tr标签
    # trs = htmlElement.xpath("//tr[@class='even']")
    # for tr in trs:
    #     print(etree.tostring(tr,encoding='utf-8').decode('utf-8'))

    # 4. 获取所有a标签的href属性
    # aList = htmlElement.xpath("//a/@href")
    # for a in aList:
    #     print("http://hr.tencent.com/"+a)

    # 5. 获取所有的职位信息(纯文本)
    trs = htmlElement.xpath("//tr[position()>1]") # position()方法 获取 下标 大于 1 的 所有tr标签
    positions = []
    for tr in trs:
        # 在某个标签下,再执行xpath函数,获取这个标签下的子孙元素
        # 那么应该在//之前加一个点,代表是在当前元素下获取
        href = tr.xpath(".//a/@href")[0]
        fullurl = 'http://hr.tencent.com/' + href
        title = tr.xpath("./td[1]//text()")[0]
        category = tr.xpath("./td[2]/text()")[0]
        nums = tr.xpath("./td[3]/text()")[0]
        address = tr.xpath("./td[4]/text()")[0]
        pubtime = tr.xpath("./td[5]/text()")[0]

        position = {
            'url': fullurl,
            'title': title,
            'category': category,
            'nums': nums,
            'address': address,
            'pubtime': pubtime
        }
        positions.append(position)

    print(positions)

ExtractElement()

beautifulsoup库

BeautifulSoup语法


## find_all的使用:
1. 在提取标签的时候,第一个参数是标签的名字。然后如果在提取标签的时候想要使用标签属性进行过滤,
    那么可以在这个方法中通过关键字参数的形式,将属性的名字以及对应的值传进去。或者是使用`attrs`属性,
    将所有的属性以及对应的值放在一个字典中传给`attrs`属性。
2. 有些时候,在提取标签的时候,不想提取那么多,那么可以使用`limit`参数。限制提取多少个。

## find与find_all的区别:
1. find:找到第一个满足条件的标签就返回。说白了,就是只会返回一个元素。
2. find_all:将所有满足条件的标签都返回。说白了,会返回很多标签(以列表的形式)。

## 使用find和find_all的过滤条件:
1. 关键字参数:将属性的名字作为关键字参数的名字,以及属性的值作为关键字参数的值进行过滤。
2. attrs参数:将属性条件放到一个字典中,传给attrs参数。

## 获取标签的属性:
1. 通过下标获取:通过标签的下标的方式。
    ```python
    href = a['href']
    ```
2. 通过attrs属性获取:示例代码:
    ```python
    href = a.attrs['href']
    ```

## string和strings、stripped_strings属性以及get_text方法:
1. string:获取某个标签下的非标签字符串。返回来的是个字符串。如果这个标签下有多行字符,那么就不能获取到了。
2. strings:获取某个标签下的子孙非标签字符串。返回来的是个生成器。
2. stripped_strings:获取某个标签下的子孙非标签字符串,会去掉空白字符。返回来的是个生成器。
4. get_text:获取某个标签下的子孙非标签字符串。不是以列表的形式返回,是以普通字符串返回。


## CSS选择器:
1. 根据标签的名字选择,示例代码如下:
    ```css
    p{
        background-color: pink;
    }
    ```
2. 根据类名选择,那么要在类的前面加一个点。示例代码如下:
    ```css
    .line{
        background-color: pink;
    }
    ```
3. 根据id名字选择,那么要在id的前面加一个#号。示例代码如下:
    ```css
    #box{
        background-color: pink;
    }
    ```
4. 查找子孙元素。那么要在子孙元素中间有一个空格。示例代码如下:
    ```css
    #box p{
        background-color: pink;
    }
    ```
5. 查找直接子元素。那么要在父子元素中间有一个>。示例代码如下:
    ```css
    #box > p{
        background-color: pink;
    }
    ```
6. 根据属性的名字进行查找。那么应该先写标签名字,然后再在中括号中写属性的值。示例代码如下:
    ```css
    input[name='username']{
        background-color: pink;
    }
    ```
7. 在根据类名或者id进行查找的时候,如果还要根据标签名进行过滤。那么可以在类的前面或者id的前面加上标签名字。示例代码如下:
    ```css
    div#line{
        background-color: pink;
    }
    div.line{
        background-color: pink;
    }
    ```

## BeautifulSop中使用css选择器:
在`BeautifulSoup`中,要使用css选择器,那么应该使用`soup.select()`方法。应该传递一个css选择器的字符串给select方法。


## 常见的四种对象:
1. Tag:BeautifulSoup中所有的标签都是Tag类型,并且BeautifulSoup的对象其实本质上也是一个Tag类型。所以其实一些方法比如find、find_all并不是BeautifulSoup的,而是Tag的。
2. NavigableString:继承自python中的str,用起来就跟使用python的str是一样的。
3. BeautifulSoup:继承自Tag。用来生成BeaufifulSoup树的。对于一些查找方法,比如find、select这些,其实还是Tag的。
4. Comment:这个也没什么好说,就是继承自NavigableString。

## contents和children:
返回某个标签下的直接子元素,其中也包括字符串。他们两的区别是:contents返回来的是一个列表,children返回的是一个迭代器。

"""

导入库

from bs4 import BeautifulSoup

bs4语法使用

from bs4 import BeautifulSoup

# html文本
html = """html文本"""

bs = BeautifulSoup(html,"lxml") # 创建一个bs4对象,传入 html数据,使用 lxml解析器来操作


## find_all()、string、stripped_strings 等方法 操作:

# 0. # 打印获取的数据
# print(bs.prettify())

# tr = bs.find_all('tr')[1]
# text = tr.string
# print(text)

# 1. 获取所有tr标签
# trs = bs.find_all('tr')
# for tr in trs:
#     print(tr)
#     print('*'*30)

# 2. 获取第2个tr标签
# tr = bs.find_all('tr',limit=2)[1]
# print(tr)

# 3. 获取所有class等于even的tr标签
# trs = bs.find_all('tr',attrs={'class':"even"})
# for tr in trs:
#     print(tr)
#     print('='*30)

# 4. 将所有id等于test,class也等于test的a标签提取出来。
# aList = bs.find_all('a',id='test',class_='test')
# aList2 = bs.find_all('a',attrs={'id':'test','class':'test'})
# for a in aList2:
#     print(a)
#     print('='*30)

# 5. 获取所有a标签的href属性 ==> 通过操作字典对象方式, 取key键名 来获取键值
# aList = bs.find_all('a')
# for a in  aList:
#     #1.通过下标操作方式
#     href = a['href']
#     print(href)
#     print('*'*30)
#
#     #2.通过attrs属性方式
#     href = a.attrs['href']
#     print(href)

# 6. 获取所有的职位信息(纯文本)
# trs = bs.find_all('tr')[1:] # 切片:除第一个外,获取所有tr元素
# movies = []
# for tr in trs:
#     movie = {}
#
#     ## 方式一: 通过 层级结构,获取一个个相对应 数据,再把数据组合添加到字典;最后把字典添加到列表;步骤繁多-操作复杂;
#     # tds = tr.find_all("td")
#     # title = tds[0].string
#     # category = tds[1].string
#     # nums = tds[2].string
#     # city = tds[3].string
#     # pubtime = tds[4].string
#     # movie['title'] = title
#     # movie['category'] = category
#     # movie['nums'] = nums
#     # movie['city'] = city
#     # movie['pubtime'] = pubtime
#     # movies.append(movie)
#
#     ## 方式二: 去tags标签、制表符、空格 信息,获取 返回列表-文本数据 ;通过下标取值; 操作简单
#     infos = list(tr.stripped_strings)
#     print('infos:{}'.format(infos))
#     movie['title'] = infos[0]
#     movie['category'] = infos[1]
#     movie['nums'] = infos[2]
#     movie['city'] = infos[3]
#     movie['pubtime'] = infos[4]
#     movies.append(movie)
#
# print(movies)

CSS选择器

## CSS选择器:  select()方法 操作

# 1. 获取所有tr标签
# trs = bs.select("tr")
# for tr in trs:
#     print(type(tr))
#     print('='*30)
#     print(tr)
#     break

# 2. 获取第2个tr标签
# tr = bs.select('tr')[1]
# print(tr)

# 3. 获取所有class等于even的tr标签
# # trs = bs.select(".even")
# trs2 = bs.select("tr[class='even']")
# for tr in trs2:
#     print(tr)

# 4. 获取所有a标签的href属性
# aList = bs.select('a')
# for a in aList:
#     href = a['href']
#     print(href)

# 5. 获取所有的职位信息(纯文本)
trs = bs.select('tr')
for tr in trs:
    infos = list(tr.stripped_strings)
    print(infos)
    print('='*30)
    print('职位名称:',infos[0])
    print('职位类别:',infos[1])
    print('人数:',infos[2])
    print('地点:',infos[3])
    print('发布时间:',infos[4])

re库

导入库

import re

re语法使用一:

import re

# 1.匹配某个字符串
# text = 'hello'
# rest = re.match('he',text)
# print(rest.group())

#2. .点,匹配任意字符
# text = "a\nd"
# ret = re.match('.',text)
# print(ret.group())


# 3. \d:匹配任意的数字( 0 - 9 )
# text = "+"
# text = "9+10"
# rest = re.match('\d',text)
# print(rest.group())

# 4. \D:匹配任意的非数字
# text  = "a2"
# rest = re.match('\D',text)
# print(rest.group())

# 5. \s:匹配空白字符(\n,\t,\r,空格)
# text = "\r"
# rest = re.match('\s',text)
# print(rest.group())


# 6. \w:匹配 a-z,A-Z,数字和下划线
# text = "adf"
# rest = re.match('\w',text)
# print(rest.group())

#7. \W 与 \w相反
# text = "|"
# rest = re.match('\W',text)
# print(rest.group())

# 8. []组合的方式,只要满足中括号中的字符,就可以匹配
# text = "0731-88888888asfa"
# ret = re.match('[\d\-]+',text)
# print(ret.group())

# 8.1. 中括号的形式代替\d
# text = "09"
# ret = re.match('[0-9]',text)
# print(ret.group())

# 8.2. 中括号的形式代替\D
# text = "1"
# ret = re.match('[^0-9]',text)
# print(ret.group())

# 8.3. 中括号的形式代替\w
# text = "_"
# ret = re.match('[a-zA-Z0-9_]',text)
# print(ret.group())

# 8.4. 中括号的形式代替\W
# text = "0"
# ret = re.match('[^a-zA-Z0-9_]',text)
# print(ret.group())

匹配多个字符

# 9. *:可以匹配0或者任意多个字符
# text = "abcd"
# rest = re.match('\s*',text)
# print(rest.group())

# 10. +:匹配1个或者多个字符
# text  = "+abcd"
# rest = re.match('\w+',text)
# print(rest.group())

# 11. ?:匹配一个或者0个 ( 1.没有 2.只有一个)
# text = "abcd"
# rest = re.match('\w?',text)
# print(rest.group())


# 12.{m}:匹配m个字符
# text = 'abcd'
# rest = re.match('\w{2}',text)
# print(rest.group())

# 13. {m,n}:匹配 m - n 个字符
# text = 'abcd'
# rest = re.match('\w{2,5}',text)
# print(rest.group())

小案例

# 14.验证手机号码
# text = "12578900980"
# rest = re.match('1[34578]\d{9}',text)
# print(rest.group())

# 15. 验证邮箱
# text = "[email protected]"
# rest = re.match('\w+@[a-z0-9]+\.[a-z]+',text)
# print(rest.group())

# 16. 验证URL
# text = "https://baike.baidu.com/item/Python/407313?fr=aladdin"
# rest = re.match('(http|https|ftp)://[^\s]+',text)  # ():匹配内容   []:匹配范围
# print(rest.group())

# 17. 验证身份证:
# text = "31131118908123230X"
# ret = re.match('\d{17}[\dxX]',text)
# ret2 = re.match('\d{17}[\d|x|X]',text)
# print(ret.group(),ret2.group())
#

# 18. ^(脱字号),表示以...开头:
# text = "hello"
# ret = re.search('^h',text)
# print(ret.group())


# 19. $:表示以...结尾:
# text = "[email protected]"
# ret = re.match('\[email protected]$',text)
# print(ret.group())


# 20. |:匹配多个字符串或者表达式:
# text = "https"
# rest = re.match('(ftp|http|https)$',text)
# print(rest.group())


# 21:贪婪模式与非贪婪模式:
# text = "0123456"
# rest = re.match('\d+',text)
# print("贪婪模式= ",rest.group())
#
# text = "

标题

" # ret = re.match('<.+?>',text) # print("非贪婪模式= ",ret.group()) # 22:匹配0-100之间的数字 # # 可以出现的:1,2,3,10,100,99 # # 有三种情况:1,99,100 # # 不可以出现的:09,101 # # text = '99' # rest = re.match('[1-9]\d?$|100$',text) # print(rest.group())

原声字符 与 转义字符

# import re

# # text = "apple price is $299"  # 匹配 $299
# # ret = re.search("\$\d+",text) # $:在正则中有特殊意义,需转义 \  =>  \$
# # print(ret.group())
#
# # r = raw = 原生的
# # text = '\n'
# # print(text)
#
#
# text = "\c" #= '\n'
# # python:'\\n' = \n
# # \\\\n =》 \\n
# # \\c
#
# # 正则表达式中:\n =
# # \\n =》 \n
# # \\c =》 \c
#
# rest = re.match(r'\\c',text)
# print(rest.group())

分组

import re

# text = "apple's price $99,orange's price is $10"
# ret = re.search('.*(\$\d+).*(\$\d+)',text)
# print('分组原字符串:',ret.group(0)) # ret.group(0) = ret.group()
# print('某个分组的值:',ret.group(1))
# print('某个分组的值:',ret.group(2))
# print("分组 = ",ret.group(1,2))
# print('所有的子分组都拿出来:',ret.groups())

find_all函数

text = "apple's price $99,orange's price is $10"
ret = re.findall('\$\d+',text) # 查找所有符合条件的值,放在列表中
print('findall()函数: ',ret)

sub函数

# text = "apple's price $99,orange's price is $10"
# ret = re.sub('\$\d+',"0",text)  # 把符合条件的值,替换
# print('sub()函数: ',ret)

html = """

职位描述:

参与公司新一代面向生命科学行业云服务应用及平台的开发。


【工作职责】

云服务软件产品的架构设计与开发

与设计、产品及前端人员沟通,保证产品的质量和开发进度

研究新兴技术,对产品进行持续优化


【职位要求】

计算机相关专业本科及以上学历

对常见数据结构和面向对象设计有深入理解

熟练掌握Python语言,3年以上实际经验

熟悉Python Web开发框架如Django

熟练掌握数据库开发和设计

基本的英文读写能力

""" # ret = re.sub('<.+?>',"",html) # print(ret)

split函数

# text = "hello&world ni hao"
# ret = re.split('[^a-zA-Z]',text)
# print('split分割函数: ',ret)

compile()预编译函数

# compile(): 预编译函数,用来写 正则 ; 调用时 直接把对象传入即可
# text = "the number is 20.50"
# # r = re.compile('\d+\.?\d*')
# r = re.compile(r"""
#     \d+ # 小数点前面的数字
#     \.? # 小数点本身
#     \d* # 小数点后面的数字
# """,re.VERBOSE)
# ret = re.search(r,text)
# print(ret.group())

数据存储

json库

"""
在 Python 中。只有基本数据类型才能转换成 JSON 格式的字符串:
      即: int 、 float 、 str 、 list 、 dict 、 tuple 。
"""

字典和列表转JSON

## 将Python对象转换为 json字符串 ; 字典和列表转JSON:
# import json
# books = [
#     {
#     'title': '钢铁是怎样练成的',
#     'price': 9.8
#     },
#     {
#     'title': '红楼梦',
#     'price': 9.9
#     }
# ]
# json_str = json.dumps(books,ensure_ascii=False)
# print(json_str)

写入json文件

## 将json数据直接 dump 到文件中:
# import json
# books = [
#     {
#     'title': '钢铁是怎样练成的',
#     'price': 9.8
#     },
#     {
#     'title': '红楼梦',
#     'price': 9.9
#     }
# ]
# with open('books.json','w',encoding='utf-8') as fp:
#     # 因为 json 在 dump 的时候,只能存放 ascii 的字符,因此会将中文进行转义,这时候我们可以使用 ensure_ascii=False 关闭这个特性
#     json.dump(books,fp,fp,ensure_ascii=False)
#

JSON转字典和列表

## 将一个json字符串load成Python对象:
# import json
# json_str = '[{"title": "钢铁是怎样练成的", "price": 9.8}, {"title": "红楼梦", "price": 9.9}]'
# books = json.loads(json_str,encoding='utf-8')
# print(type(books))
# print(books)

读取json文件

## 直接从文件中读取json:
import json
with open('books.json','r',encoding='utf-8') as fp:
    json_str = json.load(fp)
    print(json_str)

csv库

导入库

import csv

读取csv文件

##############  读取csv文件:reader()、DictReader() ###############

import csv

## 读取csv文件:
## 获取数据的时候,就要通过下表来获取数据
def read_csv_demo1():
    with open('stock.csv', 'r') as fp:
        # reader是一个迭代器
        reader = csv.reader(fp)
        # next(reader)
        for x in reader:
            name = x[3]
            volumn = x[-1]
            print({'name': name, 'volumn': volumn})


## 读取csv文件:
## 可以使用 DictReader,在获取数据的时候通过标题:
def read_csv_demo2():
    with open('stock.csv','r') as fp:
        # 使用DictReader创建的reader对象
        # 不会包含标题那行的数据
        # reader是一个迭代器,遍历这个迭代器,返回来的是一个字典。
        reader = csv.DictReader(fp)
        for x in reader:
            value = {"name":x['secShortName'],'volumn':x['turnoverVol']}
            print(value)

写入数据到csv文件

##############  写入数据到csv文件::writerow()、DictWriter() ###############

import csv

## 写入csv文件:
## writerow ,这个是写入一行。一个是 writerows ,这个是写入多行
def write_csv_demo1():
    headers = ['username', 'age', 'height']
    values = [
        ('張三', 18, 180),
        ('李四', 19, 190),
        ('王五', 20, 160)
    ]

    with open('classroom.csv', 'w', encoding='utf-8', newline='') as fp:
        writer = csv.writer(fp)
        writer.writerow(headers)
        writer.writerows(values)


## 写入csv文件:
## 使用字典的方式把数据写入进去。这时候就需要使用 DictWriter
def write_csv_demo2():
    headers = ['username', 'age', 'height']
    values = [
        {'username':'张三','age':18,'height':180},
        {'username':'李四','age':19,'height':190},
        {'username':'王五','age':20,'height':160}
    ]
    with open('classroo1.csv','w',encoding='utf-8',newline='') as fp:
        writer = csv.DictWriter(fp,headers)
        # 写入表头数据的时候,需要调用writeheader方法
        writer.writeheader()
        writer.writerows(values)

mysql库

连接数据库

import pymysql

database = {
    'host':'127.0.0.1',
    'port':3306,
    'user':'python',
    'password':'pythonvip',
    'database':'spider_db'
}

"""
# cursor.execute(sql)
# 插入、删除、更新。 都需要执行commit操作 conn.commit() 提交执行结果、conn.close()关闭连接
"""
# 连接数据库
conn = pymysql.connect(**database)
# 获取游标:拥有操作 mysql的 权限和方法
cursor = conn.cursor()
print(cursor)

# sql_insert_1 = "insert into spider_tb(name,sex,age) values('xiaoguo','女',26)" # 编写 SQL语句
# sql_insert_1 = cursor.execute(sql_insert_1) # 执行 SQL数据库 语句
# conn.commit() # 提交执行结果
# conn.close() # 关闭连接

# sql_delete = 'delete from spider whwere id = {}'
# cursor.execute(sql_delete.format('1'))
# conn.commit()
# conn.close()

# sql_update = 'update spider set name={} where name={}'
# cursor.execute(sql_delete.sql_update('C#','Java'))
# conn.commit()
# conn.close()

######## all ########
# sql_select_all = "select * from spider"
# result = cursor.execute(sql_select_all)
# print(result)
# conn.close()


######## where ########
# sql_select_where = "select name,sex,age from spider where id=2"
# result = cursor.execute(sql_select_where)
# print(result)
# conn.close()


######## fetchone ########
# sql_select_fetchone = "select * from spider"
# cursor.execute(sql_select_fetchone)
# while True:
#     result = cursor.fetchone()
#     if result:
#         print(result)
#     else:
#         break
#
# conn.close()


######## fetchall ########
# sql_select_fetchall = "select * from spider"
# cursor.execute(sql_select_fetchall)
# results = cursor.fetchall()
# for result in results:
#     print(result)
# conn.close()


######## fetchmany ########
# sql_select_fetchmany = "select * from spider"
# cursor.execute(sql_select_fetchmany)
# results = cursor.fetchmany(3)
# for result in results:
#     print(result)
# conn.close()

mongodb库

连接

import pymongo

#获取连接mongodb对象
client = pymongo.MongoClient("127.0.0.1",port= 27017)

连接数据库与表

# 获取数据库[1.有则连接,2.无则-创建并连接]
db = client.study

# 获取数据库中的集合[理解为: MySQL中的表]
collection = db.table

# 1.单行写入
# collection.insert({"username":'guokaichong'})

# 2.多行写入
# collection.insert_many([
#     {
#         "username":"aaa",
#         'age': 18
#     },
#     {
#         "username":"bbb",
#         'age': 20
#     },
#     {
#         "username":"guokaichong",
#         'age': 20,
#         'job':'python工程师'
#     },
# ])

#1.单行删除
# collection.delete_one({"username":"guokaichong"})

#2.多行删除
# collection.delete_many({"username":"hackers"})

#1.单行修改
# collection.update_one({"username":'bbb'},{"$set":{"username":"hackers"}})

#.2多行修改
# collection.update_many({'username':'aaa'},{"$set":{"username":'hackers'}})

# 1.find方法:获取集合中所以的数据
# cursor = collection.find()
# for x in cursor:
#     print(x)

### 2.获取集合中 一条数据
# result = collection.find_one({"age":20})
# print(result)

excel库

读取 Excel 文件

import xlrd

workbook = xlrd.open_workbook("成绩表.xlsx")

sheet相关操作

"""
获取 Sheet :
    一个 Excel 中可能有多个 Sheet ,那么可以通过以下方法来获取想要的 Sheet 信息:
    1. sheet_names :获取所有的 sheet 的名字。
    2. sheet_by_index :根据索引获取 sheet 对象。
    3. sheet_by_name :根据名字获取 sheet 对象。
    4. sheets :获取所有的 sheet 对象。
    5. sheet.nrows :这个 sheet 中的行数。
    6. sheet.ncols :这个 sheet 中的列数。
"""
##1.获取所有sheet 表头的名字
# sheets = workbook.sheet_names()
# print("获取所以sheet 表头的名字",sheets)

##2.根据索引获取sheet
# for index in range(3):
#     sheet = workbook.sheet_by_index(index)
#     print(type(sheet))
#     print(sheet.name)

##3.# 根据名称获取sheet
# sheet = workbook.sheet_by_name('1班')
# print(sheet.name)

##4.# 获取所有的sheet对象
# for sheet in workbook.sheets():
#     print(sheet.name)

##5.获取这个sheet中的行数和列数
# sheet0 = workbook.sheet_by_index(0)
# print("行数:%d"%sheet0.nrows)
# print('列数:%d'%sheet0.ncols)
#

Cell相关操作

"""
获取Cell及其属性:
每个 Cell 代表的是表格中的一格。以下方法可以方便获取想要的 cell :
1. sheet.cell(row,col) :获取指定行和列的 cell 对象。
2. sheet.row_slice(row,start_col,end_col) :获取指定行的某几列的cell对象。
3. sheet.col_slice(col,start_row,end_row) :获取指定列的某几行的cell对象。
4. sheet.cell_value(row,col) :获取指定行和列的值。
5. sheet.row_values(row,start_col,end_col) :获取指定行的某几列的值。
6. sheet.col_values(col,start_row,end_row) :获取指定列的某几行的值。
"""
sheet = workbook.sheet_by_index(0)

##使用cell方法获取指定的cell对象
# cell = sheet.cell(0,0) # 第几行,第几列
# print(type(cell))
# print(cell)

##使用row_slice获取第0行的1-2列的cell对象
# cells = sheet.row_slice(0,1,3) # 第几行,第几列,到第几列
# total = sum([cell.value for  cell in cells ])
# print(cells,total)

##使用col_slice获取第0列的1-2行的cell对象
# cells = sheet.col_slice(0,1,3)
# print(cells)

# cells = sheet.col_slice(1,1,sheet.nrows)
# avg = sum([cell.value for cell in cells])/len(cells)
# print(cells,avg)

# scores = sheet.col_values(1,1,sheet.nrows)
# avg = sum(scores)/len(scores)
# print(avg)

Cell的数据类型

"""
Cell的数据类型:
1. xlrd.XL_CELL_TEXT(Text) :文本类型。
2. xlrd.XL_CELL_NUMBER(Number) :数值类型。
3. xlrd.XL_CELL_DATE(Date) :日期时间类型。
4. xlrd.XL_CELL_BOOLEAN(Bool) :布尔类型。
5. xlrd.XL_CELL_BLANK :空白数据类型。
"""
sheet = workbook.sheet_by_index(0)

# xlrd.XL_CELL_TEXT(Text) :文本类型。
# cell = sheet.cell(0,0)
# print(cell.ctype)
# print(xlrd.XL_CELL_TEXT)

# xlrd.XL_CELL_NUMBER(Number) :数值类型
# cell = sheet.cell(1,1)
# print(cell.ctype)
# print(xlrd.XL_CELL_NUMBER)

# xlrd.XL_CELL_DATE(Date) :日期时间类型
# cell = sheet.cell(19,0)
# print(cell.ctype)
# print(xlrd.XL_CELL_DATE)


# xlrd.XL_CELL_BOOLEAN(Bool)
# cell = sheet.cell(19,1)
# print(cell.ctype)
# print(xlrd.XL_CELL_BOOLEAN)

# xlrd.XL_CELL_BLANK :空白数据类型
# cell = sheet.cell(19,2)
# print(cell.ctype)
# print(xlrd.XL_CELL_EMPTY)

写入Excel

写入 Excel 步骤如下:
1. 导入 xlwt 模块。
2. 创建一个 Workbook 对象。
3. 创建一个 Sheet 对象。
4. 使用 sheet.write(row,col,data) 方法把数据写入到 Sheet 下指定行和列中。如果想要在原
来 workbook 对象上添加新的 cell ,那么需要调用 put_cell 来添加。
5. 保存成 Excel 文件。
"""
# import xlwt
# import random
# workbook = xlwt.Workbook(encoding='utf-8')
# sheet = workbook.add_sheet("成绩表")
# # 添加表头
# fields = ['数学','英语','语文']
# for index,field in enumerate(fields):
#     sheet.write(0,index,field)
# # 随机的添加成绩
# for row in range(1,10):
#     for col in range(3):
#         grade = random.randint(0,100)
#         sheet.write(row,col,grade)
# workbook.save("abc.xls")

编辑 Excel 文件

"""
编辑 Excel 文件:
    如果想要在原来已经存在的 Excel 文件中添加新的行或者新的列,那么需要采
    用 put_cell(row,col,type,value,xf_index) 来添加进去,最后再放到 xlwt 创建的 workbook 中,
    然后再保存进去
"""
import xlrd
import xlwt

workbook = xlrd.open_workbook("成绩表.xlsx")
rsheet = workbook.sheet_by_index(0) # 从下标为 0 位置开始 读取 sheet表
#
# # 添加 总分成绩
# rsheet.put_cell(0,4,xlrd.XL_CELL_TEXT,"总分",None)
# for row in range(1,rsheet.nrows):
#     grade = sum(rsheet.row_values(row,1,4))
#     rsheet.put_cell(row,4,xlrd.XL_CELL_TEXT,grade,None)
#
# # 添加每个科目的平均分成绩
# total_rows = rsheet.nrows
# total_cols = rsheet.ncols
# for col in range(1,total_cols):
#     grades = rsheet.col_values(col,1,total_rows)
#     avg_grade = sum(grades)/len(grades)
#     print(type(avg_grade))
#     rsheet.put_cell(total_rows,col,xlrd.XL_CELL_NUMBER,avg_grade,None)
#
# # 重新写入一个 Excel 文件数据
# wwb = xlwt.Workbook(encoding='utf-8')
# wsheet = wwb.add_sheet("1班学生成绩")
# for row in range(rsheet.ncols):
#     for col in range(rsheet.ncols):
#         wsheet.write(row,col,rsheet.cell_value(row,col))
#
# wwb.save("abc.xls")

案例

import xlrd
import xlwt

rwb = xlrd.open_workbook("成绩表.xlsx")
rsheet = rwb.sheet_by_index(0)

# 添加总分的cell
rsheet.put_cell(0,4,xlrd.XL_CELL_TEXT,"总分",None)

# 添加总分的数据
nrows = rsheet.nrows
ncols = rsheet.ncols
for row in range(1,nrows):
    scores = rsheet.row_values(row,1,4)
    rsheet.put_cell(row,4,xlrd.XL_CELL_NUMBER,sum(scores),None)

# 添加每个科目的平均分
for col in range(1,rsheet.ncols):
    scores = rsheet.col_values(col,1,nrows)
    avg = sum(scores)/len(scores)
    rsheet.put_cell(nrows,col,xlrd.XL_CELL_NUMBER,avg,None)


# 编辑的实质:读取->编辑->写入一个新的excel文件
wwb = xlwt.Workbook(encoding='utf-8')
wsheet = wwb.add_sheet("xxx")
for row in range(rsheet.nrows):
    for col in range(rsheet.ncols):
        value = rsheet.cell_value(row,col)
        wsheet.write(row,col,value)
wwb.save("新成绩表.xls")

进阶

多线程

多线程介绍

"""
多线程介绍:
多线程是为了同步完成多项任务,通过提高资源使用效率来提高系统的效率。线程是在同一时间需
要完成多项任务的时候实现的。
最简单的比喻多线程就像火车的每一节车厢,而进程则是火车。车厢离开火车是无法跑动的,同理
火车也可以有多节车厢。多线程的出现就是为了提高效率。同时它的出现也带来了一些问题。

threading 模块是 python 中专门提供用来做多线程编程的模块。threading 模块中最常用的类是 Thread ;
使用 threading.enumerate() 函数便可以看到当前线程的数量。
"""

传统的方式

# def coding():
#     for x in range(3):
#         print('正在写代码%s'%x)
#         time.sleep(1)
#
# def drawing():
#     for x in range(3):
#         print('正在画图%s' % x)
#         time.sleep(1)
#
#
# def main():
#     coding()
#     drawing()
#
# if __name__ == '__main__':
#     main()

采用多线程的方式

# import time
# import threading
#
# def conding():
#     for x in range(3):
#         print('正在写代码%s'%threading.current_thread())
#         time.sleep(1)
#
#
# def drawing():
#     for x in range(3):
#         print('正在画图%s' % threading.current_thread())
#         time.sleep(1)
#
#
# def main():
#     t1 = threading.Thread(target=conding)
#     t2 = threading.Thread(target=drawing)
#
#     t1.start()
#     t2.start()
#
#     print(threading.enumerate()) #使用 threading.enumerate() 函数便可以看到当前线程的数量。
#
#
# if __name__ == '__main__':
#     main()

继承自 threading.Thread 类

"""
继承自 threading.Thread 类:
为了让线程代码更好的封装。可以使用 threading 模块下的 Thread 类,继承自这个类,然后实
现 run 方法,线程就会自动运行 run 方法中的代码
"""
# import threading
# import time
#
# class CodinThread(threading.Thread):
#     def run(self):
#         for x in range(3):
#             print('正在写代码%s'%threading.current_thread())
#             time.sleep(1)
#
# class DrawingThread(threading.Thread):
#     def run(self):
#         print('正在画图%s' % threading.current_thread())
#         time.sleep(1)
#
#
# def main():
#     t1 = CodinThread()
#     t2 = DrawingThread()
#
#     t1.start()
#     t2.start()
#
# if __name__ == '__main__':
#     main()

多线程共享全局变量的问题

"""
多线程共享全局变量的问题:
多线程都是在同一个进程中运行的。因此在进程中的全局变量所有线程都是可共享的。这就造成了
一个问题,因为线程执行的顺序是无序的。有可能会造成数据错误:

结果 :因为多线程运行的不确定性。因此最后的结果可能是随机的
"""
# import threading
#
# tickets = 0
# def get_ticket():
#     global tickets
#     for x in range(1000000):
#         tickets += 1
#     print('tickets:%d'%tickets)
#
# def main():
#     for x in range(2):
#         t = threading.Thread(target=get_ticket)
#         t.start()
# if __name__ == '__main__':
#     main()

锁机制

"""
锁机制:
为了解决以上使用共享全局变量的问题。 threading 提供了一个 Lock 类,这个类可以在某个线
程访问某个变量的时候加锁,其他线程此时就不能进来,直到当前线程处理完后,把锁释放了,其
他线程才能进来处理。
"""
# import threading
#
# VALUE = 0
# gLock = threading.Lock()
#
# def add_value():
#     global VALUE
#     gLock.acquire()
#     for x in range(1000000):
#         VALUE += 1
#     gLock.release()
#     print('value:%d'%VALUE)
# def main():
#     for x in range(2):
#         t = threading.Thread(target=add_value)
#         t.start()
# if __name__ == '__main__':
#     main()

Lock版本生产者和消费者模式

"""
Lock版本生产者和消费者模式:
生产者和消费者模式是多线程开发中经常见到的一种模式。生产者的线程专门用来生产一些数据,
然后存放到一个中间的变量中。消费者再从这个中间的变量中取出数据进行消费。但是因为要使用
中间变量,中间变量经常是一些全局变量,因此需要使用锁来保证数据完整性。以下是使
用 threading.Lock 锁实现的“生产者与消费者模式”的一个例子:
# """
# import threading
# import random
# import time
#
# gMoney = 1000
# gLock = threading.Lock()
# gTimes = 0
#
# class Producer(threading.Thread):
#     def run(self):
#         global gMoney
#         global gLock
#         global gTimes
#         while True:
#             money = random.randint(100, 1000)
#             gLock.acquire()
#             if gTimes >= 10:
#                 gLock.release()
#                 break
#             gMoney += money
#             print('%s当前存入%s元钱,剩余%s元钱' % (threading.current_thread(), money, gMoney))
#             gTimes += 1
#             time.sleep(0.5)
#             gLock.release()
#
# class Consumer(threading.Thread):
#     def run(self):
#         global gMoney
#         global gLock
#         global gTimes
#         while True:
#             money = random.randint(100, 500)
#             gLock.acquire()
#             if gMoney > money:
#                 gMoney -= money
#                 print('%s当前取出%s元钱,剩余%s元钱' % (threading.current_thread(), money, gMoney))
#                 time.sleep(0.5)
#             else:
#                 if gTimes >= 10:
#                     gLock.release()
#                     break
#                 print("%s当前想取%s元钱,剩余%s元钱,不足!" % (threading.current_thread(),money,gMoney))
#             gLock.release()
#
# def main():
#     for x in range(5):
#         Consumer(name='消费者线程%d'%x).start()
#
#     for x in range(5):
#         Producer(name='生产者线程%d'%x).start()
#
# if __name__ == '__main__':
#     main()

Condition版的生产者与消费者模式

Condition版的生产者与消费者模式:
Lock 版本的生产者与消费者模式可以正常的运行。但是存在一个不足,在消费者中,总是通
过 while True 死循环并且上锁的方式去判断钱够不够。上锁是一个很耗费CPU资源的行为。因此
这种方式不是最好的。还有一种更好的方式便是使用 threading.Condition 来实
现。 threading.Condition 可以在没有数据的时候处于阻塞等待状态。一旦有合适的数据了,还可
以使用 notify 相关的函数来通知其他处于等待状态的线程。这样就可以不用做一些无用的上锁和
解锁的操作。可以提高程序的性能。首先对 threading.Condition 相关的函数做个介
绍, threading.Condition 类似 threading.Lock ,可以在修改全局数据的时候进行上锁,也可以
在修改完毕后进行解锁。以下将一些常用的函数做个简单的介绍:

1. acquire :上锁。
2. release :解锁。
3. wait :将当前线程处于等待状态,并且会释放锁。可以被其他线程使
用 notify 和 notify_all 函数唤醒。被唤醒后会继续等待上锁,上锁后继续执行下面的代
码。
4. notify :通知某个正在等待的线程,默认是第1个等待的线程。
5. notify_all :通知所有正在等待的线程。 notify 和 notify_all 不会释放锁。并且需要在 release 之前调用。
# import threading
# import random
# import time
#
# gMoney = 1000
# gCondition = threading.Condition()
# gTimes = 0
# gTotalTimes = 5
#
# class Producer(threading.Thread):
#     def run(self):
#         global gMoney
#         global gCondition
#         global gTimes
#         while True:
#             money = random.randint(100, 1000)
#             gCondition.acquire()
#             if gTimes >= gTotalTimes:
#                 gCondition.release()
#                 print('当前生产者总共生产了%s次'%gTimes)
#                 break
#             gMoney += money
#             print('%s当前存入%s元钱,剩余%s元钱' % (threading.current_thread(), money, gMoney))
#             gTimes += 1
#             time.sleep(0.5)
#             gCondition.notify_all()
#             gCondition.release()
#
# class Consumer(threading.Thread):
#     def run(self):
#         global gMoney
#         global gCondition
#         while True:
#             money = random.randint(100, 500)
#             gCondition.acquire()
#             # 这里要给个while循环判断,因为等轮到这个线程的时候
#             # 条件有可能又不满足了
#             while gMoney < money:
#                 if gTimes >= gTotalTimes:
#                     gCondition.release()
#                     return
#                 print('%s准备取%s元钱,剩余%s元钱,不足!'%(threading.current_thread(),money,gMoney))
#                 gCondition.wait()
#             gMoney -= money
#             print('%s当前取出%s元钱,剩余%s元钱' % (threading.current_thread(), money, gMoney))
#             time.sleep(0.5)
#             gCondition.release()
#
# def main():
#     for x in range(5):
#         Consumer(name='消费者线程%d'%x).start()
#
#     for x in range(2):
#         Producer(name='生产者线程%d'%x).start()
#
# if __name__ == '__main__':
#     main()

Queue线程安全队列

"""
Queue线程安全队列:
在线程中,访问一些全局变量,加锁是一个经常的过程。如果你是想把一些数据存储到某个队列
中,那么Python内置了一个线程安全的模块叫做 queue 模块。Python中的queue模块中提供了同
步的、线程安全的队列类,包括FIFO(先进先出)队列Queue,LIFO(后入先出)队列
LifoQueue。这些队列都实现了锁原语(可以理解为原子操作,即要么不做,要么都做完),能够
在多线程中直接使用。可以使用队列来实现线程间的同步。相关的函数如下:
1. 初始化Queue(maxsize):创建一个先进先出的队列。
2. qsize():返回队列的大小。
3. empty():判断队列是否为空。
4. full():判断队列是否满了。
5. get():从队列中取最后一个数据。
6. put():将一个数据放到队列中。
"""
from queue import Queue
import time
import threading

# q = Queue(4)

# for x in range(4):
#    print( q.put(x))
#
# for x in range(4):
#     print(q.get())

### 小案例;
# def set_value(q):
#     index = 0
#     while True:
#         q.put(index)
#         index += 1
#         time.sleep(3)
#
# def get_value(q):
#     while True:
#         print(q.get())
#
# def main():
#     q = Queue(4)
#     t1 = threading.Thread(target=set_value,args=[q])
#     t2 = threading.Thread(target=get_value,args=[q])
#
#     t1.start()
#     t2.start()
#
#
# if __name__ == '__main__':
#     main()

selenium自动化

介绍与应用

"""
动态网页数据抓取
什么是AJAX:
    AJAX(Asynchronouse JavaScript And XML)异步JavaScript和XML。过在后台与服务器进行少
    量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对
    网页的某部分进行更新。传统的网页(不使用Ajax)如果需要更新内容,必须重载整个网页页面。
    因为传统的在传输数据格式方面,使用的是 XML 语法。因此叫做 AJAX ,其实现在数据交互基本
    上都是使用 JSON 。使用AJAX加载的数据,即使使用了JS,将数据渲染到了浏览器中,在 右键->
    查看网页源代码 还是不能看到通过ajax加载的数据,只能看到使用这个url加载的html代码。

获取ajax数据的方式:
    1. 直接分析ajax调用的接口。然后通过代码请求这个接口。
    2. 使用Selenium+chromedriver模拟浏览器行为获取数据。
"""
"""
Selenium+chromedriver获取动态数据:
Selenium 相当于是一个机器人。可以模拟人类在浏览器上的一些行为,自动处理浏览器上的一些
行为,比如点击,填充数据,删除cookie等。 chromedriver 是一个驱动 Chrome 浏览器的驱动程
序,使用他才可以驱动浏览器。当然针对不同的浏览器有不同的driver。以下列出了不同浏览器及
其对应的driver
"""

快速入门

"""
快速入门:
现在以一个简单的获取百度首页的例子来讲下 Selenium 和 chromedriver 如何快速入门:
"""
# import os
# from selenium import webdriver
#
#
# # chromedriver的绝对路径
# driver_path = os.path.abspath('phantomjs.exe')  # chrome.exe
# # 初始化一个driver,并指定chromedriver的路径
# driver = webdriver.PhantomJS(executable_path=driver_path)
# # 请求网页
# driver.get('https://www.baidu.com')
# # 通过page_source 获取网页源代码
# print(driver.page_source)

selenium常用操作

"""
selenium常用操作:
关闭页面:
1. driver.close() :关闭当前页面。
2. driver.quit() :退出整个浏览器。
"""
# from selenium import webdriver
# import time
#
# webdriver = webdriver.PhantomJS()
# webdriver.get('https://www.baidu.com/')
# time.sleep(5)
# webdriver.quit()

定位元素

find_element_by_id :根据id来查找某个元素。等价于:
    submitTag = driver.find_element_by_id('su')
    submitTag1 = driver.find_element(By.ID,'su')
    
find_element_by_class_name :根据类名查找元素。 等价于:
    submitTag = driver.find_element_by_class_name('su')
    submitTag1 = driver.find_element(By.CLASS_NAME,'su')
    
find_element_by_name :根据name属性的值来查找元素。等价于:
    submitTag = driver.find_element_by_name('email')
    submitTag1 = driver.find_element(By.NAME,'email') 
    
    
find_element_by_tag_name :根据标签名来查找元素。等价于:
    submitTag = driver.find_element_by_tag_name('div')
    submitTag1 = driver.find_element(By.TAG_NAME,'div')  
        
find_element_by_xpath :根据xpath语法来获取元素。等价于:
    submitTag = driver.find_element_by_xpath('//div')
    submitTag1 = driver.find_element(By.XPATH,'//div')

find_element_by_css_selector :根据css选择器选择元素。等价于:
    submitTag = driver.find_element_by_css_selector('//div')
    submitTag1 = driver.find_element(By.CSS_SELECTOR,'//div')

# 要注意,find_element 是获取第一个满足条件的元素。find_elements 是获取所有满足条件的元素。

# 1. 如果只是想要解析网页中的数据,那么推荐将网页源代码扔给lxml来解析。因为lxml底层使用的是C语言,所以解析效率会更高。
# 2. 如果是想要对元素进行一些操作,比如给一个文本框输入值,或者是点击某个按钮,那么就必须使用selenium给我们提供的查找元素的方法。
"""
# from selenium import webdriver
# from lxml import etree
# from selenium.webdriver.common.by import By
#
# driver_path = r"C:\Users\Administrator\AppData\Local\Google\Chrome\Application\chrome.exe"
# driver = webdriver.PhantomJS(executable_path=driver_path)
# driver.get('https://www.baidu.com/')
#
# # html = etree.HTML(driver.page_source)
# # html.xpath("")
#
# # inputTag = driver.find_element_by_id('kw')
# # inputTag = driver.find_element_by_name('wd')
# inputTag = driver.find_elements(By.CSS_SELECTOR,".quickdelete-wrap > input")[0]
# print(inputTag)
# inputTag.send_keys('python')

操作表单元素

"""
操作表单元素:
1. 操作输入框:分为两步。第一步:找到这个元素。第二步:使用 send_keys(value) ,将数据填充进去
2. 操作checkbox:因为要选中 checkbox 标签,在网页中是通过鼠标点击的。因此想要选
    中 checkbox 标签,那么先选中这个标签,然后执行 click 事件。
3. 选择select:select元素不能直接点击。因为点击后还需要选中元素。这时候selenium就专门
    为select标签提供了一个类 selenium.webdriver.support.ui.Select 。将获取到的元素当成参
    数传到这个类中,创建这个对象。以后就可以使用这个对象进行选择了。
4. 操作按钮:操作按钮有很多种方式。比如单击、右击、双击等。这里讲一个最常用的。就是点击。直接调用 click 函数就可以了

# 常见的表单元素:
    input type='text/password/email/number'
    # buttton、input[type='submit']
    # checkbox:input='checkbox'
    # select:下拉列表
"""

操作输入框

### 操作输入框
# from selenium import webdriver
# import time
#
# driver_path = r"D:\ProgramApp\chromedriver\chromedriver.exe"
# driver = webdriver.Chrome(executable_path=driver_path)
# driver.get('https://www.baidu.com/')
#
# inputTag = driver.find_element_by_id('kw')
# inputTag.send_keys('python')
#
# time.sleep(3)
#
# inputTag.clear()

操作checkbox

### 操作checkbox
# from selenium import webdriver
# import time
#
# driver_path = r"D:\ProgramApp\chromedriver\chromedriver.exe"
# driver = webdriver.Chrome(executable_path=driver_path)
# driver.get('https://www.douban.com/')
#
# rememberBtn = driver.find_element_by_name('remember')
# rememberBtn.click()

操作 select标签

### 操作 select标签
# from selenium import webdriver
# from selenium.webdriver.support.ui import Select
#
# driver_path = r"D:\ProgramApp\chromedriver\chromedriver.exe"
# driver = webdriver.Chrome(executable_path=driver_path)
# driver.get('http://www.dobai.cn/')
#
# selectBtn = Select(driver.find_element_by_name('jumpMenu'))
# # selectBtn.select_by_index(1)
# # selectBtn.select_by_value("http://m.95xiu.com/")
# selectBtn.select_by_visible_text("95秀客户端")

案例: 输入-信息,点击搜索

# from selenium import webdriver
# from selenium.webdriver.support.ui import Select
#
# driver_path = r"D:\ProgramApp\chromedriver\chromedriver.exe"
# driver = webdriver.Chrome(executable_path=driver_path)
# driver.get('https://www.baidu.com/')
#
# inputTag = driver.find_element_by_id('kw')
# inputTag.send_keys('python')
#
# submitTag = driver.find_element_by_id('su')
# submitTag.click()

行为链

"""
行为链:
有时候在页面中的操作可能要有很多步,那么这时候可以使用鼠标行为链类 ActionChains 来完
成。比如现在要将鼠标移动到某个元素上并执行点击事件。

还有更多的鼠标相关的操作。
click_and_hold(element):点击但不松开鼠标。
context_click(element):右键点击。
double_click(element):双击。 更多方法请参考:http://selenium�python.readthedocs.io/api.html
"""
# from selenium import webdriver
# from selenium.webdriver.common.action_chains import ActionChains
# import time
#
# driver_path = r"D:\ProgramApp\chromedriver\chromedriver.exe"
# driver = webdriver.PhantomJS(executable_path=driver_path)
# driver.get('https://www.baidu.com/')
#
# inputTag = driver.find_element_by_id('kw')
# submitBtn = driver.find_element_by_id('su')
#
# actions = ActionChains(driver)
# actions.move_to_element(inputTag)
# actions.send_keys_to_element(inputTag,'python')
# actions.move_to_element(submitBtn)
# actions.click(submitBtn)
# actions.perform() # 执行-代码

Cookie操作

"""
Cookie操作:
    1. 获取所有的 cookie :
        for cookie in driver.get_cookies():
        print(cookie)
    2. 根据cookie的key获取value:
        value = driver.get_cookie(key)
    3. 删除所有的cookie:
        driver.delete_all_cookies()
    4. 删除某个 cookie :
        driver.delete_cookie(key)
"""
# from selenium import webdriver
# from selenium.webdriver.common.action_chains import ActionChains
# import time
#
# driver_path = r"D:\ProgramApp\chromedriver\chromedriver.exe"
# driver = webdriver.Chrome(executable_path=driver_path)
# driver.get('https://www.baidu.com/')
#
# for cookie in driver.get_cookies():
#     print(cookie)
#
# print('='*30)
#
# print(driver.get_cookie("PSTM"))
#
# driver.delete_cookie("PSTM")
# print('='*30)
# # print(driver.get_cookie('PSTM'))
# driver.delete_all_cookies()

页面等待

"""
页面等待:
现在的网页越来越多采用了 Ajax 技术,这样程序便不能确定何时某个元素完全加载出来了。如果
实际页面等待时间过长导致某个dom元素还没出来,但是你的代码直接使用了这个WebElement,
那么就会抛出NullPointer的异常。为了解决这个问题。所以 Selenium 提供了两种等待方式:一种
是隐式等待、一种是显式等待。
  隐式等待: driver.implicitly_wait(time)、 
  显式等待:WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "myDynamicElement")))
  一些其他的等待条件:presence_of_element_located:某个元素已经加载完毕了。
                    presence_of_all_emement_located:网页中所有满足条件的元素都加载完毕了。
                    element_to_be_cliable:某个元素是可以点击了。
"""
# from selenium import webdriver
# from selenium.webdriver.support.ui import WebDriverWait
# from selenium.webdriver.support import expected_conditions as EC
# from selenium.webdriver.common.by import By
#
# driver_path = r"D:\ProgramApp\chromedriver\chromedriver.exe"
# driver = webdriver.Chrome(executable_path=driver_path)
# driver.get('https://www.douban.com/')
#
# ## 隐式等待:调用 driver.implicitly_wait 。那么在获取不可用的元素之前,会先等待10秒中的时间。
# # driver.implicitly_wait(20)
#
# """
# 显示等待:显示等待是表明某个条件成立后才执行获取元素的操作。也可以在等待的时候指定
# 一个最大的时间,如果超过这个时间那么就抛出一个异常。显示等待应该使
# 用 selenium.webdriver.support.excepted_conditions 期望的条件
# 和 selenium.webdriver.support.ui.WebDriverWait 来配合完成
# """
# element = WebDriverWait(driver,10).until(
#     EC.presence_of_element_located((By.ID,'form_email'))
# )
# print(element)

切换页面

"""
切换页面:
有时候窗口中有很多子tab页面。这时候肯定是需要进行切换的。 selenium 提供了一个叫
做 switch_to_window 来进行切换,具体切换到哪个页面,可以从 driver.window_handles 中找到

# 虽然在窗口中切换到了新的页面。但是driver中还没有切换。
# 如果想要在代码中切换到新的页面,并且做一些爬虫。
# 那么应该使用driver.switch_to_window来切换到指定的窗口
# 从driver.window_handlers中取出具体第几个窗口
# driver.window_handlers是一个列表,里面装的都是窗口句柄。
# 他会按照打开页面的顺序来存储窗口的句柄。
"""
# from selenium import webdriver
#
# driver_path = r"D:\ProgramApp\chromedriver\chromedriver.exe"
# driver = webdriver.Chrome(executable_path=driver_path)
# driver.get('https://www.baidu.com/')
#
# # driver.get("https://www.douban.com/")
# driver.execute_script("window.open('https://www.douban.com/')")
# print(driver.window_handles)
# driver.switch_to_window(driver.window_handles[1])
#
# print(driver.current_url)
# print(driver.page_source)

设置代理ip

"""
设置代理ip:
有时候频繁爬取一些网页。服务器发现你是爬虫后会封掉你的ip地址。这时候我们可以更改代理
ip。更改代理ip,不同的浏览器有不同的实现方式。
"""
# from selenium import webdriver
#
# driver_path = r"D:\ProgramApp\chromedriver\chromedriver.exe"
# options = webdriver.ChromeOptions()
# options.add_argument("--proxy-server=http://60.17.239.207:31032")
#
# driver = webdriver.Chrome(executable_path=driver_path,chrome_options=options)
#
# driver.get("http://httpbin.org/ip")

WebElement 元素

"""
WebElement 元素:
from selenium.webdriver.remote.webelement import WebElement 类是每个获取出来的元素的所属
类。
有一些常用的属性:
1. get_attribute:这个标签的某个属性的值。
2. screentshot:获取当前页面的截图。这个方法只能在 driver 上使用。
    driver 的对象类,也是继承自 WebElement
"""
from selenium import webdriver
from selenium.webdriver.remote.webelement import WebElement

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

submitBtn = driver.find_element_by_id('su')
print(type(submitBtn))
print(submitBtn.get_attribute("value"))
driver.save_screenshot('baidu.png')

常见反反爬高级技术

字体反爬

字符验证码

点击验证码

滑动验证码

常见加密算法

python执行js

js调试查找加密方式

移动设备APP高级爬虫

appnium

mitmproxy

爬虫框架

scrapy

scrapy_redis

pyspider

你可能感兴趣的:(2020-04-01)