一天掌握python爬虫

时间:2023-03-09 19:51:42
一天掌握python爬虫

一天掌握python爬虫日记:

(小爬虫,NO 我们是大蜘蛛 )

数据抓取:

requests:
requests 的底层实现其实就是 urllib
开源地址:https://github.com/kennethreitz/requests
中文文档 API: http://docs.python-requests.org/zh_CN/latest/index.html
基本GET请求(headers参数 和 parmas参数):
import requests

url = "http://www.baidu.com"
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()
# res = requests.get(url=url, params=kw, headers=headers)
# 也可以这么写
res = requests.request(method="GET", url=url, params=kw, headers=headers)
# print(res.text) # 查看响应内容,response.text 返回的是Unicode格式的数据
print(res.content.decode("utf-8")) # 查看响应内容,response.content返回的字节流数据
print(res.url)  # 查看完整url地址
print(res.encoding)  # 查看响应头部字符编码
print(res.status_code)  # 查看响应码

StringIO与BytesIO:
# StringIO在内存中读写str。
# BytesIO 是在内存中读写bytes类型的二进制数据
# 通过requests获取网络上图片的大小
from io import BytesIO, StringIO
from PIL import Image  # pip install pillow 安装

img_url = "http://imglf1.ph.126.net/pWRxzh6FRrG2qVL3JBvrDg==/6630172763234505196.png"
res = requests.get(img_url)
f = BytesIO(res.content)
img = Image.open(f)
print(img.size)

基本POST请求(data参数)
import requests

url = 'https://www.lagou.com/jobs/positionAjax.json?city=%E5%8C%97%E4%BA%AC&needAddtionalResult=false'
headers = {
    'User-Agent': 'User-Agent:Mozilla/5.0(Windows;U;WindowsNT6.1;en-us)AppleWebKit/534.50(KHTML,likeGecko)Version/5.1Safari/534.50',
    'Referer': 'https://www.lagou.com/jobs/list_python?city=%E5%8C%97%E4%BA%AC&cl=false&fromSearch=true&labelWords=&suginput=',
    'Accept-Language': 'zh-Hans-CN,zh-Hans;q=0.5'
}
data = {
    'first': 'true',
    'kd': 'python',
    'pn': 1
}
resp = requests.post(url=url, data=data, headers=headers)
# print(resp.content.decode('utf-8'))
print(resp.json())  # # 如果是json文件可以直接显示,返回字典类型

代理(proxies参数)
proxies = {
    'http': '39.137.2.206:8080'

}
# 如果代理需要使用HTTP Basic Auth,可以使用下面这种格式:
# proxy = { "http": "name:pass@61.158.163.130:16816" }
res = requests.get("http://httpbin.org/ip", proxies=proxies)
print(res.text)
# {
#   "origin": "39.137.2.206"
# }
也可以通过本地环境变量 HTTP_PROXY 和 HTTPS_PROXY 来配置代理:

export HTTP_PROXY="http://39.137.2.206:8080"
export HTTPS_PROXY="https://12.34.56.79:9527"

web客户端验证

如果是Web客户端验证,需要添加 auth = (账户名, 密码)

import requests

auth=('test', '123456')
response = requests.get('http://192.168.199.107', auth = auth)
print (response.text)

Cookies:

res = requests.get(url="http://www.baidu.com/")
print(res.cookies)  # <RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
# print(res.cookies.get_dict()) # {'BDORZ': '27315'}
cookiedict = requests.utils.dict_from_cookiejar(res.cookies)
print(cookiedict)  # {'BDORZ': '27315'}

session:

url = 'http://www.renren.com/PLogin.do'
headers = {
    'User-Agent': 'User-Agent:Mozilla/5.0(Windows;U;WindowsNT6.1;en-us)AppleWebKit/534.50(KHTML,likeGecko)Version/5.1Safari/534.50',
}
data = {
    # 'email': 'xxxx@qq.com',
    # 'password': 'xxxx'
}
session = requests.session()  # 创建session对象,可以保存Cookie值
# 发送附带用户名和密码的请求,并获取登录后的Cookie值,保存在ssion里
session.post(url=url, data=data, headers=headers)
# session包含用户登录后的Cookie值,可以直接访问那些登录后才可以访问的页面
response = session.get("http://www.renren.com/410043129/profile")
print(response.text)

处理HTTPS请求 SSL证书验证:
# 跳过证书验证,把 verify 设置为 False
resp = requests.get('https://www.12306.cn/mormhweb/', verify=False)
print(resp.content.decode('utf-8'))

==========================================================================================
urllib库的基本使用:
在 python2 中,urllib 被分为urllib,urllib2等

request:

from urllib import request

# urlopen

# res = request.urlopen(url='http://www.baidu.com')

# print(res.read())  # 读取文件全部内容,返回字符串
# print(res.readlines())
# print(res.getcode()) # 200

# Request执行更复杂的操作

headers = {"User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"}
req = request.Request(url='http://www.baidu.com', headers=headers,method='GET')
# 也可以通过调用Request.add_header() 添加/修改一个特定的header
req.add_header("Connection", "keep-alive")
# 也可以通过调用Request.get_header()来查看header信息
print(req.get_header(header_name='Connection'))  # keep-alive
res = request.urlopen(req)

# print(res.read().decode())
print(res.code)  # 可以查看响应状态码

# parse
from urllib import parse

url = 'http://www.baidu.com/s?'
wd = {'wd': '爬虫'}
ps = parse.urlencode(wd)  # 通过urllib.urlencode()方法,将字典键值对按URL编码转换,从而能被web服务器接受
url = url + ps
print(url)  # http://www.baidu.com/s?wd=%E7%88%AC%E8%99%AB

url = parse.parse_qs(url)
print(url)  # {'http://www.baidu.com/s?wd': ['爬虫']}
# 通过parse.unquote方法,把 URL编码字符串,转换回原先字符串
url = parse.unquote("wd=%E7%88%AC%E8%99%AB")
print(url)  # wd=爬虫

url = 'http://www.baidu.com/s;hello;123?wd=sss&name=qqq#a'
result = parse.urlparse(url)
print(result)
# ParseResult(scheme='http', netloc='www.baidu.com', path='/s', params='hello;123', query='wd=sss&name=qqq', fragment='a')

result2 = parse.urlsplit(url)
print(result2)
# SplitResult(scheme='http', netloc='www.baidu.com', path='/s;hello;123', query='wd=sss&name=qqq', fragment='a')

# 处理HTTPS请求 SSL证书验证
from urllib import request
import ssl

context = ssl._create_unverified_context()  # 表示忽略未经核实的SSL证书认证
url = "https://www.12306.cn/mormhweb/"
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"}

req = request.Request(url, headers=headers)
# 在urlopen()方法里 指明添加 context 参数
res = request.urlopen(url=req, context=context)
print(res.read().decode())

Handler处理器 和 自定义Opener:

from urllib import request

# 构建一个HTTPHandler 处理器对象,支持处理HTTP请求
# debuglevel=1参数,还会将 Debug Log 打开,程序在执行的时候,会把收包和发包的报头在屏幕上自动打印出来
http_handler = request.HTTPHandler(debuglevel=1)
# http_handler = request.HTTPSHandler() # 支持处理HTTPS请求
# 调用urllib.request.build_opener()方法,创建支持处理HTTP请求的opener对象
opener = request.build_opener(http_handler)
req = request.Request('http://www.baidu.com/')
# 调用自定义opener对象的open()方法,发送request请求
res = opener.open(req)
print(res.code)

ProxyHandler处理器(代理设置):

from urllib import request
httpproxy_handler = request.ProxyHandler({'http': '39.137.2.206:8080'})
req = request.Request('http://httpbin.org/ip')
opener = request.build_opener(httpproxy_handler)

# res = opener.open(req) # 只有使用opener.open()方法发送请求才使用自定义的代理,而urlopen()则不使用自定义代理
# opener应用到全局
request.install_opener(opener)
res = request.urlopen(req)
print(res.read())

cookiejar库 和 HTTPCookieProcessor处理器:
from http import cookiejar
from urllib import request

# 构建一个CookieJar对象实例来保存cookie
# cookiejar = cookiejar.CookieJar()

cookiejar = cookiejar.MozillaCookieJar('cookie.txt')

cookiejar.load(ignore_discard=True)  # 读取 True 包括短暂性的cookie
# 使用HTTPCookieProcessor()来创建cookie处理器对象,参数为CookieJar()对象
handler = request.HTTPCookieProcessor(cookiejar)
opener = request.build_opener(handler)
opener.open('http://www.baidu.com')

# cookiejar.save(ignore_discard=True) # 保存

cookieStr = ""
for item in cookiejar:
    print(item.name+'='+item.value)

urlretrieve:

from urllib import request

img_url = "http://n.sinaimg.cn/news/1_img/upload/cf3881ab/138/w1535h1003/20181029/MOVg-hnaivxq1076478.jpg"
request.urlretrieve(img_url,filename='img.jpg')

================================================================================================

数据提取:

正则re模块:
import re

text = 'hello'
# .匹配任意字符
com = re.compile('.+')
# match从头开始匹配
ret = re.match(com, text)
print(ret.group())
# \d匹配任意数字

# \D 匹配任意非数字

# \s 任意空白字符(\r  \n  \n)

# \w a-z A-Z 数字 下划线

# \W  与\w相反

# [ ]组合,只要满足中括号中的某一项就算成功

# ^[0-9] 非0-9 (脱字号)   ^a 以a开头

# + 一个或多个

# * 零个或多个

# ? 零个或一个

# {n} n个

# {m,n} m-n个

# $以  结尾

# | 匹配多个字符或表达式

# 贪婪模式(默认)  非贪婪模式
text = '<h1>标题1</h1>'
ret = re.match('<.+?>', text) # 非贪婪模式
# ret = re.match('<.+>', text)
print(ret.group())

# 匹配0-100之间的数字
text = '0'
ret = re.match('[1-9]\d?$|100$|0$', text)
print(ret.group())

# 转义字符 \
text = 'apple price is $299'
ret = re.search('\$(\d+)', text)
print(ret.group(1))

# 打印\n
# text = '\\n'
text = r'\n'  # r原生的
print(text)

text = '\\n' # =>\n
# python \\\\n => \\n
# 正则 \\n=> \n
# ret = re.match('\\\\n', text)
ret = re.match(r'\\n', text)
print(ret.group())

# compile编译
# pattern = re.compile('\d+\.?\d+')
# 多行
pattern = re.compile("""
    \d+
    \.?
    \d+
""", re.VERBOSE)
text = 'the number is 20.50'
s = re.search(pattern, text)
print(s.group())
'''
Pattern 对象的一些常用方法主要有:
        match 方法:从起始位置开始查找,一次匹配
        search 方法:从任何位置开始查找,一次匹配
        findall 方法:全部匹配,返回列表
        finditer 方法:全部匹配,返回迭代器
        split 方法:分割字符串,返回列表
        sub 方法:替换
'''
# match

pattern = re.compile(r'\d+')

m = pattern.match('one123a', 3, 7)
print(m)  # <_sre.SRE_Match object; span=(3, 6), match='123'>
print(m.group())  # 123
print(m.start())  # 3
print(m.end())  # 6
print(m.span())  # (3, 6) 返回匹配成功的整个子串的索引

# search

text = "apple's price $99,orange's price is $10"
pattern = re.compile('.*(\$\d+).*(\$\d+)', re.I)  # # re.I 表示忽略大小写
ret = pattern.search(text)
# print(ret.group(0))   相当于print(ret.group())
print(ret.group(1))
print(ret.group(2))
# 所有的组
print(ret.groups())  # ('$99', '$10')
print(ret.span())  # (0, 39) # 起始位置和结束位置

# findall
ret = re.findall('\$\d+', text)
print(ret)  # ['$99', '$10']

# finditer
res = re.finditer('\$\d+', text)
for m in res:
    print(m.group(), m.span())  # $99 (14, 17)  # $10 (36, 39)

# split

text1 = 'hello2world ni hao '
ret = re.split(' |\d', text1)  # 空格或数字
print(ret)  # ['hello', 'world', 'ni', 'hao', '']

# sub

ret = re.sub('\$\d+', '0', text)
print(ret)  # apple's price 0,orange's price is 0

pattern = re.compile(r'(\w+) (\w+)')  # [A-Za-z0-9]
s = 'a b,c d'
res = pattern.sub('123', s)
print(res)  # 123,123
res = pattern.sub(r'\2 \1', s)  # 引用分组
print(res)  # b a,d c

print(pattern.sub(lambda m: m.group(2) + m.group(1), s))  # ba,dc
print(pattern.sub(lambda m: m.group(2) + m.group(1), s, 1))  # ba,c d  # 最多替换一次

# 匹配中文

title = '你好,hello,世界'
pattern = re.compile(r'[\u4e00-\u9fa5]+')
result = pattern.findall(title)

print(result)  # ['你好', '世界']

=======================================================================================
XPath 开发工具

开源的XPath表达式编辑工具:XMLQuire(XML格式文件可用)
    Chrome插件 XPath Helper
    Firefox插件 try XPath

最常用的路径表达式:
表达式     描述
nodename     选取此节点的所有子节点。
/     从根节点选取。
//     从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
.     选取当前节点。
..     选取当前节点的父节点。
@     选取属性。

lxml库:

lxml 是 一个HTML/XML的解析器,主要的功能是如何解析和提取 HTML/XML 数据。
lxml python 官方文档:http://lxml.de/index.html

'''
最常用的路径表达式:
表达式     描述
nodename     选取此节点的所有子节点。
/     从根节点选取。 子元素
//     从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
.     选取当前节点。
..     选取当前节点的父节点。
@     选取属性。

路径表达式     结果
/bookstore/book[1]     选取属于 bookstore 子元素的第一个 book 元素。
/bookstore/book[last()]     选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1]     选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()<3]     选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang]     选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang=’eng’]     选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。

/text() 文本内容
//input[starts-with(@name,'name1')]     查找name属性中开始位置包含'name1'关键字的页面元素
//input[contains(@name,'na')]         查找name属性中包含na关键字的页面元素

通配符     描述
*     匹配任何元素节点。
@*     匹配任何属性节点。
'''
# lxml

from lxml import etree

text = '''
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a> # 注意,此处缺少一个 </li> 闭合标签
     </ul>
 </div>
 
'''

# html = etree.HTML(text)  # 利用etree.HTML,将字符串解析为HTML文档
# res = etree.tostring(html)  # 按字符串序列化HTML文档
# print(res) # lxml 可以自动修正 html 代码,例子里不仅补全了 li 标签,还添加了 body,html 标签
# print(type(html)) # <class 'lxml.etree._Element'>  # 显示etree.parse() 返回类型

# parser = etree.HTMLParser(encoding='utf-8')
# html = etree.parse(source='tencent.html', parser=parser)  # 读取外部文件
# print(etree.tostring(html, pretty_print=True))  # 美观的打印

# 实例测试

html = etree.HTML(text)
res = html.xpath('//li/@class')  # 获取<li> 标签的所有 class属性
print(res)  # ['item-0', 'item-1', 'item-inactive', 'item-1', 'item-0']
res = html.xpath('//li/a[@href="link1.html"]')  # 获取<li>标签下hre 为 link1.html 的 <a> 标签
print(res)  # [<Element a at 0x7ff5a1409388>]
res = html.xpath('//li/a/@href')  # 获取 <li> 标签下的<a>标签里的所有 class
print(res)  # ['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']
res = html.xpath('//li[last()]/a/@href')  # 获取最后一个 <li> 的 <a> 的 href
print(res)  # ['link5.html']
res = html.xpath('//li[last()-1]/a/@href')  # 倒数第二个 <li> 的 <a> 的 href
print(res)  # ['link4.html']

res = html.xpath('//li[position()>1 and position()<3]/a/text()')
print(res)  # ['second item']

res = html.xpath('//li[contains(@class,"inactive")]/a/text()')  # 包含
print(res)  # ['third item']

res = html.xpath('//li/a[starts-with(@href,"link5")]/text()')  # 开头
print(res)  # ['fifth item']

==================================================================
JsonPath

"""
JsonPath 是一种信息抽取类库,是从JSON文档中抽取指定信息的工具
    官方文档:http://goessner.net/articles/JsonPath

XPath     JSONPath     描述
/     $     根节点
.     @     现行节点
/     .or[]     取子节点
..     n/a     取父节点,Jsonpath未支持
//     ..     就是不管位置,选择所有符合条件的条件
*     *     匹配所有元素节点
@     n/a     根据属性访问,Json不支持,因为Json是个Key-value递归结构,不需要。
[]     []     迭代器标示(可以在里边做简单的迭代操作,如数组下标,根据内容选值等)
|     [,]     支持迭代器中做多选。
[]     ?()     支持过滤操作.
n/a     ()     支持表达式计算
()     n/a     分组,JsonPath不支持
"""
import requests
import jsonpath
import json

url = 'http://www.lagou.com/lbs/getAllCitySearchLabels.json'
res = requests.get(url=url)
html = res.text

jsonobj = json.loads(html)  # 把json格式字符串转换成python对象
citylist = jsonpath.jsonpath(jsonobj, '$..name')  # 从根节点开始,匹配name节点

# print(citylist) # ['安庆', '澳门特别行政区', '鞍山',
# print(type(citylist)) # <class 'list'>
content = json.dumps(citylist, ensure_ascii=False)
# print(content) # ["安庆", "澳门特别行政区", "鞍山",
with open('city.json', 'w') as f:
    f.write(content)
==========================================================
"""
lxml 只会局部遍历,而Beautiful Soup 是基于HTML DOM的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml。
官方文档:http://beautifulsoup.readthedocs.io/zh_CN/v4.4.0
Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:

Tag  HTML 中的一个个标签,有两个重要的属性,是 name 和 attrs
    NavigableString 标签的内容
    BeautifulSoup 表示的是一个文档的内容
    Comment 注释内容,其输出的内容不包括注释符号

"""
from bs4 import BeautifulSoup

html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
soup = BeautifulSoup(html, 'lxml')  # 指定lxml解析器。
# soup = BeautifulSoup(open('tencent.html'),'lxml') # 打开本地 HTML 文件的方式来创建对象
# print(soup.prettify())  # 格式化输出 soup 对象的内容

# Tag

# print(soup.name) # [document] #soup 对象本身比较特殊,它的 name 即为 [document]
# print(soup.head.name) # head #对于其他内部标签,输出的值便为标签本身的名称
# print(soup.p.attrs) # {'class': ['title'], 'name': 'dromouse'}
# print(soup.p['class']) # ['title']
# print(soup.p.get('class')) # ['title']
# soup.p['class'] = "newClass"
# print(soup.p) # 可以对这些属性和内容等等进行修改
# del soup.p['class'] # 对这个属性进行删除
# print(soup.p)

# NavigableString
# print(soup.p.string)  # The Dormouse's story
# print(type(soup.p.string))  # <class 'bs4.element.NavigableString'>

# BeautifulSoup
# print(type(soup))  # <class 'bs4.BeautifulSoup'>
# print(soup.attrs)  # {} # 文档本身的属性为空

# Comment
# print(soup.a.string)  # Elsie
# print(type(soup.a.string))  # <class 'bs4.element.Comment'>

# 遍历文档树
'''

# 直接子节点 :.contents .children 属性.content
print(soup.head.contents) # [<title>The Dormouse's story</title>]
print(soup.head.children) # <list_iterator object at 0x7f4a07c70240>
for child in soup.body.children:
    print(child)

# 所有子孙节点: .descendants 属性,递归循环
for child in soup.descendants:
    print(child)

# 节点内容: .string 属性
print(soup.title.string)  # The Dormouse's story
'''
# 搜索文档树
# find_all(name, attrs, recursive, text, **kwargs)
# print(soup.find_all('b')) # 传字符串 # 查找文档中所有的<b>标签:
# import re
# print(soup.find_all(re.compile('^b')))
# print(soup.find_all(['a','b'])) # 文档中所有<a>标签和<b>标签:
# print(soup.find_all('a',limit=2)) # 提取2个

# print(soup.find_all('p', attrs={'class': 'title'}))
# print(soup.find_all('p', class_='title'))
# print(soup.find_all('p'))
# print(list(soup.find_all('p')[1].stripped_strings)) # 去空格
# print(list(soup.find_all('p')[1].strings))  # 内容
# print(soup.find_all('p')[0].string)  # 节点内容 是一个生成器
# print(type(soup.find_all('p')[1].get_text()))  # 内容 # <class 'str'>

# print(soup.find_all(text='Lacie')) # ['Lacie'] #  搜文档中的字符串内容

# CSS选择器
# CSS时,标签名不加任何修饰,类名前加.,id名前加  #
# print(soup.select('title')) # [<title>The Dormouse's story</title>]
# print(soup.select('.sister'))
# print(soup.select('#link1'))
# print(soup.select('p #link1')) # 查找 p 标签中,id 等于 link1的内容,二者需要用空格分开
# print(soup.select('head > title')) # 直接子标签查找,则使用 > 分隔
# print(soup.select('a[class="sister"]'))
# print(soup.select('p a[href="http://example.com/elsie"]'))

# print(soup.select('title')[0].get_text())  # The Dormouse's story
# print(soup.select('title')[0].string)  # The Dormouse's story
# print(soup.select('title')[0].stripped_strings)  # <generator object stripped_strings at 0x7f01587f8620>
# print(soup.select('title')[0].strings)  # <generator object _all_strings at 0x7f71966fd620>

===============================================================
多线程:

import threading
import time

def coding():
    for i in range(3):
        print('{}在写代码{}'.format(threading.current_thread(), i))
        time.sleep(1)

def drawing():
    for i in range(3):
        print('{}在写绘画{}'.format(threading.current_thread(), i))
        time.sleep(1)

def main():
    threading.Thread(target=coding).start()
    threading.Thread(target=drawing).start()
    print(threading.enumerate())  # 所有线程枚举

class CodingThread(threading.Thread):
    def run(self):
        for i in range(3):
            print('{}在写代码{}'.format(threading.current_thread(), i))
            time.sleep(1)

class DrawingThread(threading.Thread):
    def run(self):
        for i in range(3):
            print('{}在写绘画{}'.format(threading.current_thread(), i))
            time.sleep(1)

def main1():
    CodingThread().start()
    DrawingThread().start()

if __name__ == '__main__':
    # main()
    main1()

# 修改全局变量

VALUE = 0
gLock = threading.Lock()

def add():
    global VALUE
    gLock.acquire()  # 加锁
    for i in range(100000):
        VALUE += 1
    gLock.release()  # 解锁
    print("value:{}".format(VALUE))

def main():
    for i in range(2):
        t = threading.Thread(target=add)
        t.start()

if __name__ == '__main__':
    main()

'''
Queue,是线程安全的
    初始化: class Queue.Queue(maxsize) FIFO 先进先出
    包中的常用方法:
        Queue.qsize() 返回队列的大小
        Queue.empty() 如果队列为空,返回True,反之False
        Queue.full() 如果队列满了,返回True,反之False
        Queue.full 与 maxsize 大小对应
        Queue.get([block[, timeout]])获取队列,timeout等待时间
    Queue.task_done() #向队列中已完成的元素发送join信号
    创建一个“队列”对象
        import Queue
        myqueue = Queue.Queue(maxsize = 10)
    将一个值放入队列中
        myqueue.put(10)
    将一个值从队列中取出
        myqueue.get()
'''
from queue import Queue

q = Queue(maxsize=4)
print(q.maxsize)  # 4
print(q.empty())  # True
q.put(1, block=True)  # 默认True
q.put(2)
print(q.qsize())  # 2
q.put(3)
q.put(4)
print(q.full())  # True
print(q.get(block=True))  # 1 # 默认True

多线程任务:

import threading
import time
from queue import Queue

DOCS = ""

class ThreadSpider(object):
    def __init__(self):
        self.url_queue = Queue()
        self.html_queue = Queue()

def get_total_url(self):
        for i in range(10):
            self.url_queue.put(i)

def parse_url(self):
        while self.url_queue.not_empty:   # 一个人任务添加到队列
            url = self.url_queue.get()
            time.sleep(1)
            self.html_queue.put(url)
            # 向任务已经完成的队列发送一个信号
            # 主要是给join用的,每次get后需要调用task_done,直到所有任务都task_done,join才取消阻塞
            self.url_queue.task_done()

def save(self):
        while self.html_queue.not_empty:
            html = self.html_queue.get()
            time.sleep(1)
            global DOCS
            DOCS += str(html)+' '
            self.html_queue.task_done()

def run(self):
        thread_list = []
        thread_url = threading.Thread(target=self.get_total_url)
        thread_list.append(thread_url)

for i in range(10):
            thread_parse = threading.Thread(target=self.parse_url)
            thread_list.append(thread_parse)

thread_save = threading.Thread(target=self.save)
        thread_list.append(thread_save)

for t in thread_list:
            t.setDaemon(True) # 为每个进程设置为后台进程,效果是主进程退出子进程也会退出,解决程序结束无法退出的问题
            t.start()

self.url_queue.join()  # 主线程等待子线程
        self.html_queue.join()

if __name__ == '__main__':
    ThreadSpider().run()
    print(DOCS)

多线程任务2:

DOCS = ""

class ProThread(threading.Thread):
    def __init__(self, url_queue, html_queue, *args, **kwargs):
        super(ProThread, self).__init__(*args, **kwargs)
        self.url_queue = url_queue
        self.html_queue = html_queue

def run(self):
        while True:
            if self.url_queue.empty():
                break
            url = self.url_queue.get()
            self.parse_url(url)

def parse_url(self, url):
        time.sleep(1)
        self.html_queue.put(url)
        self.url_queue.task_done()

class ConThread(threading.Thread):
    def __init__(self, url_queue, html_queue, *args, **kwargs):
        super(ConThread, self).__init__(*args, **kwargs)
        self.url_queue = url_queue
        self.html_queue = html_queue

def run(self):
        while True:
            if self.html_queue.empty() and self.url_queue.empty():
                break
            html = self.html_queue.get()
            global DOCS
            DOCS += str(html)+' '
            self.html_queue.task_done()

def main():
    url_queue = Queue(100)
    html_queue = Queue(1000)
    for i in range(10):
        url_queue.put(i)

for i in range(5):
        t = ProThread(url_queue, html_queue)
        t.setDaemon(True)
        t.start()
    for i in range(5):
        t = ConThread(url_queue, html_queue)
        t.setDaemon(True)
        t.start()
    url_queue.join()
    html_queue.join()

if __name__ == '__main__':
    main()
    print(DOCS)

=======================================================================================
Selenium:

'''
Selenium是一个Web的自动化测试工具
Selenium 官方参考文档:http://selenium-python.readthedocs.io/index.html

chromedriver:http://chromedriver.storage.googleapis.com/index.html
查看driver和浏览器版本:http://chromedriver.storage.googleapis.com/2.31/notes.txt
geckodriver:https://github.com/mozilla/geckodriver/releases

chmod +x chromedriver
sudo mv chromedriver /usr/bin/
#解压,加上执行权限,移动到/usr/bin/文件夹下。(复制则将mv改为cp)

PhantomJS 曾经的知名无头浏览器
'''
import time
from selenium import webdriver

# 调用键盘按键操作时需要引入的Keys包
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By

# driver = webdriver.Firefox(executable_path=r'xx/geckodriver') # 填写驱动的位置
options = webdriver.FirefoxOptions()
options.add_argument('-headless')  # chrome一样的设置
driver = webdriver.Firefox(options=options)  # 已在环境变量指定

driver.get('http://www.baidu.com')  # get方法会一直等到页面被完全加载,然后才会继续程序
# print(driver.page_source) # 打印页面内容
# data = driver.find_element_by_id('wrapper').text  # 获取页面名为 wrapper的id标签的文本内容
# print(data)
# print(driver.title)  # 打印页面的标题 百度一下,你就知道
# driver.save_screenshot('baidu.png')  # 生成当前页面快照并保存
# driver.find_element_by_id('kw').send_keys(u'爬虫')

# driver.find_element(By.ID, value='kw').send_keys(u'爬虫')
# driver.find_element_by_id('su').click() # 模拟点击
# driver.save_screenshot('爬虫.png')

# driver.find_element_by_id('kw').clear() # 清除输入框内容
driver.find_element(By.ID, value='kw').send_keys(u'长城')
driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a')# ctrl+a 全选输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'x')# ctrl+x 剪切输入框内容
driver.find_element_by_id('kw').clear() # 清除输入框内容
driver.find_element_by_id('kw').send_keys(Keys.CONTROL,'v')# 粘贴
driver.find_element_by_id('su').send_keys(Keys.RETURN) # 模拟Enter回车键
time.sleep(3)
driver.save_screenshot('长城.png')
print(driver.current_url) # 获取当前url
# print(driver.get_cookies()) # 获取当前页面Cookie
driver.close()
driver.quit()

"""
Selenium 的 WebDriver提供了各种方法来寻找元素

<input type="text" name="user-name" id="passwd-id" />
# 获取id标签值 By ID
element = driver.find_element_by_id("passwd-id")
# 获取name标签值 By Name
element = driver.find_element_by_name("user-name")
# 获取标签名值 By Tag Name
element = driver.find_elements_by_tag_name("input")
# 也可以通过XPath来匹配 By XPath
element = driver.find_element_by_xpath("//input[@id='passwd-id']")
# By Class Name
<div class="cheese"><span>Cheddar</span></div><div class="cheese"><span>Gouda</span></div>
cheeses = driver.find_elements_by_class_name("cheese")
# By Link Text
<a href="http://www.google.com/search?q=cheese">cheese</a>
cheese = driver.find_element_by_link_text("cheese")
# By Partial Link Text
<a href="http://www.google.com/search?q=cheese">search for cheese</a>
cheese = driver.find_element_by_partial_link_text("cheese")
# By CSS
<div id="food"><span class="dairy">milk</span><span class="dairy aged">cheese</span></div>
cheese = driver.find_element_by_css_selector("#food span.dairy.aged")

获取元素的属性值:
submitBtn=driver.find_element_by_id('su')
print(type(submitBtn))
# <class 'selenium.webdriver.firefox.webelement.FirefoxWebElement'>
print(submitBtn.get_attribute('value'))# 百度一下

鼠标动作链:
#导入 ActionChains 类
from selenium.webdriver import ActionChains

# 鼠标移动到 ac 位置
ac = driver.find_element_by_xpath('element')
ActionChains(driver).move_to_element(ac).perform() # perform执行 比如遇到下拉框要先移动到具体位置再点击

# 在 ac 位置单击
ac = driver.find_element_by_xpath("elementA")
ActionChains(driver).move_to_element(ac).click(ac).perform()

# 在 ac 位置双击
ac = driver.find_element_by_xpath("elementB")
ActionChains(driver).move_to_element(ac).double_click(ac).perform()

# 在 ac 位置右击
ac = driver.find_element_by_xpath("elementC")
ActionChains(driver).move_to_element(ac).context_click(ac).perform()

# 在 ac 位置左键单击hold住
ac = driver.find_element_by_xpath('elementF')
ActionChains(driver).move_to_element(ac).click_and_hold(ac).perform()

# 将 ac1 拖拽到 ac2 位置
ac1 = driver.find_element_by_xpath('elementD')
ac2 = driver.find_element_by_xpath('elementE')
ActionChains(driver).drag_and_drop(ac1, ac2).perform()

# 导入 Select 类
from selenium.webdriver.support.ui import Select

填充表单:
select.select_by_index(1) # index 索引从 0 开始
select.select_by_value("0") # value是option标签的一个属性值,并不是显示在下拉框中的值
select.select_by_visible_text(u"客户端") # visible_text是在option标签文本的值,是显示在下拉框的值
select.deselect_all() 全取消
弹窗处理:
alert = driver.switch_to_alert()
页面切换:
driver.switch_to.window("this is window name")
使用 window_handles 方法来获取每个窗口的操作对象:
for handle in driver.window_handles:
    driver.switch_to_window(handle)

页面前进和后退:
driver.forward()     #前进
driver.back()        # 后退
Cookies

获取页面每个Cookies值:
for cookie in driver.get_cookies():
    print "%s -> %s" % (cookie['name'], cookie['value'])
删除Cookies
# By name
driver.delete_cookie("CookieName")
# all
driver.delete_all_cookies()
隐式等待

隐式等待比较简单,就是简单地设置一个等待时间,单位为秒,等待时间后找不到,抛异常。

from selenium import webdriver

driver = webdriver.Chrome()
driver.implicitly_wait(10) # seconds
driver.get("http://www.xxxxx.com/loading")
myDynamicElement = driver.find_element_by_id("myDynamicElement")
当然如果不设置,默认等待时间为0。

显式等待
显式等待指定某个条件,然后设置最长等待时间。如果在这个时间还没有找到元素,那么便会抛出异常了。
from selenium import webdriver
from selenium.webdriver.common.by import By
# WebDriverWait 库,负责循环等待
from selenium.webdriver.support.ui import WebDriverWait
# expected_conditions 类,负责条件出发
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("http://www.xxxxx.com/loading")
try:
    # 页面一直循环,直到 id="myDynamicElement" 出现
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "myDynamicElement"))
    )
finally:
    driver.quit()

如果不写参数,程序默认会 0.5s 调用一次来查看元素是否已经生成,如果本来元素就是存在的,那么会立即返回。
一些内置的等待条件:
title_is  检查页面标题的期望
title_contains 标题包含
presence_of_element_located # 某个元素加载进来
visibility_of_element_located # 元素是否存在于一个页面和可见
visibility_of
url_to_be(url) 当前的url是否是url
presence_of_all_elements_located
text_to_be_present_in_element
text_to_be_present_in_element_value((By.id,'xxx'),'yyy')  xxx的内容是否是yyy  
frame_to_be_available_and_switch_to_it
invisibility_of_element_located
element_to_be_clickable  是否可点击
staleness_of
element_to_be_selected
element_located_to_be_selected
element_selection_state_to_be
element_located_selection_state_to_be
alert_is_present
"""

鼠标示例:

import time
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains

option = webdriver.FirefoxOptions()
option.add_argument('-headless')
driver = webdriver.Firefox(options=option)
driver.get('http://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,'abc')
actions.move_to_element(submitBtn)
actions.click(submitBtn)
actions.perform() # 执行上面的动作
time.sleep(3)
driver.save_screenshot('abc.png')
driver.close()
driver.quit()

cookie示例:

from selenium import webdriver

options = webdriver.FirefoxOptions()
options.add_argument('-headless')
driver = webdriver.Firefox(options=options)
driver.get('http://www.baidu.com/')
for cookie in driver.get_cookies():
    print(cookie['name']+'='+cookie['value'])

print('+'*30)
print(driver.get_cookie('BD_UPN'))
driver.delete_cookie('BD_UPN')
print(driver.get_cookie('BD_UPN'))
driver.delete_all_cookies()

driver.close()
driver.quit()

等待的示例:
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

options = webdriver.FirefoxOptions()
options.add_argument('-headless')
driver = webdriver.Firefox(options=options)
driver.get('http://www.baidu.com/')

# 隐式等待,等待时间后找不到,抛异常
# driver.implicitly_wait(5)
# driver.find_element_by_id('abcd')
# 显式等待
WebDriverWait(driver,5).until(
    # 某个元素加载进来
    EC.presence_of_all_elements_located((By.ID,'abcd'))
)

driver.close()
driver.quit()

调用js示例:

from selenium import webdriver
import time

options = webdriver.FirefoxOptions()
options.add_argument('-headless')
driver = webdriver.Firefox(options=options)
# driver.get('http://www.baidu.com/')
# driver.execute_script('window.open("http://www.dpuban.com")')

# print(driver.window_handles)  # 获取每个窗口的操作对象
# driver.switch_to.window(driver.window_handles[0])  # 页面切换
# print(driver.current_url)

# driver.get("https://movie.douban.com/typerank?type_name=剧情&type=11&interval_id=100:90&action=")
# time.sleep(5)
# driver.execute_script('document.documentElement.scrollTop=10000') # 向下滚动10000像素
# time.sleep(5)
# driver.save_screenshot('douban.png')

driver.get("https://www.baidu.com/")
# 给搜索输入框标红的javascript脚本
# js = 'var q=document.getElementById("kw");q.style.border="2px solid red";'
# driver.execute_script(js)
# driver.save_screenshot("redbaidu.png")
# js隐藏元素,将获取的图片元素隐藏
img = driver.find_element_by_xpath("//*[@id='lg']/img")
driver.execute_script('$(arguments[0]).fadeOut()', img)
time.sleep(5)
# 向下滚动到页面底部
# driver.execute_script("$('.scroll_top').click(function(){$('html,body').animate({scrollTop: '0px'}, 800);});")
driver.save_screenshot("nullbaidu.png")

driver.close()
driver.quit()

设置代理示例:

from selenium import webdriver
from selenium.webdriver.common.proxy import Proxy,ProxyType

proxy=Proxy({
    'proxyType': ProxyType.MANUAL,   # 手动设置
    'httpProxy': '61.183.233.6:54896',
    'noProxy': ''
})
options = webdriver.FirefoxOptions()
options.add_argument('-headless')
driver = webdriver.Firefox(options=options,proxy=proxy)
driver.get('http://httpbin.org/ip')
print(driver.page_source)

============================================================================================

"""
Tesseract
Tesseract 是一个 OCR 库,目前由 Google 赞助(Google 也是一家以 OCR 和机器学习技术闻名于世的公司)。Tesseract 是目前公认最优秀、最精确的开源 OCR 系统,除了极高的精确度,Tesseract 也具有很高的灵活性。它可以通过训练识别出任何字体,也可以识别出任何 Unicode 字符。
安装Tesseract
Windows 系统
下载可执行安装文件https://code.google.com/p/tesseract-ocr/downloads/list安装。
Linux 系统
可以通过 apt-get 安装: $sudo apt-get install tesseract-ocr
# 安装训练数据(equ为数学公式包)
sudo apt-get install tesseract-ocr-eng tesseract-ocr-chi-sim  tesseract-ocr-equ
Mac OS X系统
用 Homebrew(http://brew.sh/)等第三方库可以很方便地安装 brew install tesseract
安装pytesseract
Tesseract 是一个 Python 的命令行工具,不是通过 import 语句导入的库。安装之后,要用 tesseract 命令在 Python 的外面运行
pip install pytesseract

tesseract --list-langs可以查看当前支持的语言,chi_sim表示支持简体中文
tesseract -l chi_sim paixu.png paixu
"""
'''

import pytesseract
from PIL import Image

image = Image.open('a.png')
text = pytesseract.image_to_string(image)
print(text)

from PIL import Image
import pytesseract

# 设置tesseract的位置
# pytesseract.pytesseract.tesseract_cmd = r'E:\Program Files (x86)\Tesseract-OCR\tesseract.exe'
image=Image.open('b.png')
# 设置识别语言库
text=pytesseract.image_to_string(image, lang='chi_sim')
print(text)

from PIL import Image
import subprocess

image = Image.open('b.png')
subprocess.call(['tesseract', '-l', 'chi_sim', 'b.png', 'zhongwen']) #前三个参赛执行的命令, b.png要识别的图片,zhongwen保存的txt文件名

with open('zhongwen.txt', 'r') as f:
    print(f.read())
'''

from PIL import Image
import subprocess

image = Image.open('b.png')
# 对图片进行阈值过滤(低于143的置为黑色,否则为白色)
image = image.point(lambda x: 0 if x < 143 else 255)
# 重新保存图片
image.save('b1.png')
# 调用系统的tesseract命令对图片进行OCR识别
# subprocess.call(["tesseract", 'a1.png', "output"])
subprocess.call(['tesseract', '-l', 'chi_sim', 'b1.png', 'zhongwen'])
# 打开文件读取结果
with open("zhongwen.txt", 'r') as f:
    print(f.read())

===================================================================================