一天掌握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 任意空白字符(
)
# 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))
# 打印
# text = '\n'
text = r'
' # r原生的
print(text)
text = '\n' # =>
# python \\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 esseract.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())
===================================================================================