上网原理
1、爬虫概念
爬虫是什麽?
蜘蛛,蛆,代码中,就是写了一段代码,代码的功能从互联网中提取数据
互联网:
节点就是url(统一资源定位符),有很多a链接组成
互联网爬虫:
写代码,模拟浏览器访问url,并且从里面提取指定的内容
都有哪些语言可以实现爬虫?
1.php。号称世界上最优美的语言,php天生对多进程多线程支持的不好
2.java。是python最主要的竞争对手,也说它不好,java语言比较臃肿,重构起来难度太大
3.c,c++。也能够实现,不是最好的选择,只能说是能力的体现
4.python,号称世界上最美丽的语言,代码简洁,学习成本低,支持的模块多,scrapy爬虫框架
通用爬虫
百度、360、搜狗、谷歌、必应等,搜索引擎
(1)从互联网上抓取所有的数据
(2)对数据进行处理
(3)对用户提供检索服务
百度如何爬取新的内容?
(1)主动的将域名提交给百度
(2)在其他网站中设置友情链接
(3)自己会和dns服务商进行合作
robots协议:
就是一个君子协议,淘宝就不允许百度抓取
这个协议就是一个文件,要放到网站的根目录
咱写的爬虫程序,你就不用遵从了
排名:
(1)根据一个值排名,pagerank,(seo)
(2)竞价排名,为浙西事件
缺点:
(1)抓取的很多数据都是没用的
(2)不能根据需求进行爬取
聚焦爬虫
根据自己的需求,提取指定的数据
我们学习的就是聚焦爬虫
如何实现抓取指定的数据:
(1)每一个网页都有自己的唯一的url
(2)网页都是由html组成的
(3)网页的传输使用的都是http、https协议爬虫的思路:
(1)要爬取的网页的url
(2)写代码模拟浏览器发送http请求
(3)解析网页内容,字符串处理,根据规则提取数据
开发环境
windows、linux都可以
python 3.x 64位
编辑工具:pycharm sublime
整体内容介绍:
(1)模拟浏览器发送请求
urllib.request urllib.parse requests
(2)解析内容
正则表达式、bs4、xpath、jsonpath
(3)动态html
selenium+phantomjs
(4)scrapy框架的学习
(5)scrapy-redis的组件
(6)涉及到的爬虫-反爬虫-反反爬虫的内容
User-Agent、代理、验证码、动态数据
最终:理论上,只要浏览器能够访问的数据,你的程序就能访问
模拟登录
2、http协议
上网原理:看图形
http和https的区别:
-
https://www.cnblogs.com/wqhwe/p/5407468.html
**
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
http协议 https://www.cnblogs.com/10158wsj/p/6762848.html
一个完整的url
http://www.baidu.com:80/index.html?username=goudan&password=123456&lala=xixi#dudu
协议 域名 端口号 请求的资源 get参数 锚点
锚点:可以实现同一个页面的跳转
请求:
请求行、请求头、空行
常见请求头
accept:浏览器通过这个头告诉服务器,它所支持的数据类型
Accept-Charset: 浏览器通过这个头告诉服务器,它支持哪种字符集
Accept-Encoding:浏览器通过这个头告诉服务器,支持的压缩格式(重要)
Accept-Language:浏览器通过这个头告诉服务器,它的语言环境
Host:浏览器通过这个头告诉服务器,想访问哪台主机
If-Modified-Since: 浏览器通过这个头告诉服务器,缓存数据的时间
Referer:浏览器通过这个头告诉服务器,客户机是哪个页面来的 防盗链(重要)
Connection:浏览器通过这个头告诉服务器,请求完后是断开链接还是何持链接
User-Agent:客户端的类型
cookie:和身份有关的
响应:状态行、响应头、实体内容
常见响应头
Location: 服务器通过这个头,来告诉浏览器跳到哪里
Server:服务器通过这个头,告诉浏览器服务器的型号
Content-Encoding:服务器通过这个头,告诉浏览器,数据的压缩格式
Content-Length: 服务器通过这个头,告诉浏览器回送数据的长度
Content-Language: 服务器通过这个头,告诉浏览器语言环境
Content-Type:服务器通过这个头,告诉浏览器回送数据的类型
Refresh:服务器通过这个头,告诉浏览器定时刷新
Content-Disposition: 服务器通过这个头,告诉浏览器以下载方式打数据
Transfer-Encoding:服务器通过这个头,告诉浏览器数据是以分块方式回送的
**Expires: -1 **控制浏览器不要缓存
Cache-Control: no-cache
Pragma: no-cache
3、抓包工具
sublime安装和使用
- (1)安装插件管理器,packagecontrol
- (2)安装插件
按:ctrl + shift + p
输入pci,点击installpackage即可
搜索想要安装的插件,敲enter即可安装 - (3)卸载插件
按:ctrl + shift + p
输入remove,选中remove package即可
抓包:
谷歌浏览器自带抓包工具
network,点击看详情
response.headers 响应头部
request.headers 请求头
query_string 请求字符串,get参数
formadata 请求参数,post参数
response:响应内容
fiddler软件
专业的抓包软件
配置:
正常步骤:options==》https==》capture https connects==>action==>trust root certificate
windows有问题:https://blog.csdn.net/d1240673769/article/details/74298429/ubuntu下配置 http://fiddler.wikidot.com/mono
配置完毕,重启fiddler即可
如何使用
见文档
4、urllib库
是干什么的?是python用来模拟http发送请求的,是python自带的一个模块
python2.x :urllib,urllib2
python3.x :urllib
urllib.request : 发送请求、获取响应
urlopen(url=xxx, data=xxx) : 发送get,直接将参数拼接到url的后面,发送post,就需要data参数了
response对象:
read() :
读取的为二进制数据
字符串类型《====》二进制类型
encode() :
字符串格式转化为二进制格式,不写代表utf8,写gbk就代表gbk
decode() :
二进制转为字符串,不写就是utf8,写gbk就是gbk
readlines() :
读取为列表格式,按行读取
getcode() :
获取状态码
geturl() :
获取请求的url
getheaders() :
返回响应头部,列表里面有元组
urlretrieve() :
发送请求,并且将响应信息直接写入到文件中
urllib.parse :
用来处理数据
quote() :
对url里面的非法字符进行编码,字母、数字、下划线、&、://
unquote() :
url解码
urlencode() :
传递一个字典,将字典转化为
query_stirng ,
并且里面的非法字符得到编码
5、构建请求对象
User-Agent : 客户端浏览器类型
定制请求头部,将自己伪装成pc浏览器
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
# 构建请求对象
request = urllib.request.Request(url=url, headers=headers)
fiddler使用文档
1、配置
2、使用
左边:所有的请求
<> : 返回的是html文档
图片标记:代表是图片
{json} : 返回json格式
{css} : css文件
js: js文件
右边:查看某个请求的详细信息
inspectors
右上:这个请求所有的请求信息
raw:请求的原始信息
webforms:请求所带参数,
query_string代表get参数,formdata代
表post参数
右下:所有的响应信息
raw:查看详细的响应信息
json:如果返回的是json数据,在这里
查看
清除所有请求
removeall 清除所有请求
file==》capture 关闭或者启动抓包
快捷键:
select html : 所有的html请求
select js : 所有的js请求
?baidu : 选中所有带有baidu的请求
cls : 清除所有的请求
地址编码
import urllib.parse
# url = 'http://www.baidu.com/index.html?name=狗蛋'
# str1 = urllib.parse.quote(url)
url = 'http://www.baidu.com/index.html?'
data = {
'username':'狗蛋',
'pwd':'jsjsjs',
'height':'175',
}
string1 = urllib.parse.urlencode(data)
print(string1)
url+=string1
print(url)
图片保存
#
import urllib.request
url = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1531147620161&di=cc9bfc90e915669e609905a13b01affd&imgtype=0&src=http%3A%2F%2Fnres.ingdan.com%2Fuploads%2F20151210%2F1449737241395228.jpg'
response = urllib.request.urlopen(url)
urllib.request.urlretrieve(url,'tu.jpg')
网页文件down
import urllib.request
url = 'http://www.sougou.com/'
response = urllib.request.urlopen(url)
with open('sougouq.html','w',encoding='utf-8') as fp:
fp.write(response.read().decode())
print(response.getcode())
post请求
#百度翻译初级入门
import urllib.request
import urllib.parse
post_url = 'http://fanyi.baidu.com/v2transapi'
#data
# word = input('请输入')
word = 'river'
formdata = {
'from': 'en',
'to':'zh',
'query': word,
'transtype': 'realtime',
'simple_means_flag': '3',
'sign': '555962.825483',
'token': '8d393681119b809e14f9636ea55337c5',
}
formdata = urllib.parse.urlencode(formdata).encode('utf8')
headers = {
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Content-Length': '123',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Cookie': 'BAIDUID=9AB6C0FBB652B8E5C0EB2116B4794D8A:FG=1; BIDUPSID=9AB6C0FBB652B8E5C0EB2116B4794D8A; PSTM=1519631202; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; BDUSS=DJsdjlXaDlrZm5TUjdkY0NjSU05SWdaM2dxYlh4dFpwcXlQWnJtVFFSa2JKUnBiQUFBQUFBJCQAAAAAAAAAAAEAAAC8V7VPdGltZbXEcml2ZXIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABuY8lobmPJaW; H_PS_PSSID=26522_1458_21108_18560_22073; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; to_lang_often=%5B%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%2C%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%5D; from_lang_often=%5B%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%2C%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%5D; PSINO=2; locale=zh; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1531202878,1531203779,1531203784; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1531203784',
'Host': 'fanyi.baidu.com',
'Origin': 'http://fanyi.baidu.com',
'Referer': 'http://fanyi.baidu.com/?aldtype=16047',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'
}
#构建请求对象
# request = urllib.request.Request(url=post_url,headers=headers)
# #获取相应
# resposne = urllib.request.urlopen(request,data=formdata)
# print(resposne.read().decode('utf8'))
request = urllib.request.Request(url=post_url,headers=headers)
response = urllib.request.urlopen(request,data=formdata)
print(response.read().decode('utf8'))
get请求
import urllib.request
import urllib.parse
url = 'https://www.baidu.com/s?'
# word = input('请输入')
word = '烟台'
data = {
'ie':'utf8',
'wd':word
}
quit_string = urllib.parse.urlencode(data)
url+=quit_string
headers = {
'User-Agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Mobile Safari/537.36'
}
request = urllib.request.Request(url=url,headers=headers)
response = urllib.request.urlopen(request)
with open( '{}.html'.format(word),'w',encoding='utf-8') as fp:
fp.write(response.read().decode('utf8'))
ajax get请求
# ajax-get
import urllib.request
import urllib.parse
url = 'https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&'
print('每页显示二十条数据')
# page = input('请输入你要几页的数据')
page = 4
start = (page-1)*20
limit = 20
data = {
'start':start,
'limit':limit,
}
qury_string = urllib.parse.urlencode(data)
url +=qury_string
headers = {
'User-Agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Mobile Safari/537.36'
}
request = urllib.request.Request(url = url,headers=headers)
response = urllib.request.urlopen(request)
print(response.read().decode('utf8'))
ajax post 请求
# ajax-post
# kfc
import urllib.request
import urllib.parse
post_url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname'
data = {
'cname':'北京',
'pageIndex':'1',
'pid':'',
'pageSize':'10',
}
data = urllib.parse.urlencode(data).encode('utf8')
headers = {
'User-Agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Mobile Safari/537.36'
}
request = urllib.request.Request(url=post_url,headers=headers)
resposne = urllib.request.urlopen(request,data=data)
print(resposne.read().decode('utf8'))
爬虫2
1模拟各种请求方式
get请求
import urllib.request
import urllib.parse
url = 'https://www.baidu.com/s?'
word = input('请输入要查询的内容:')
# get参数写到这里
data = {
'ie': 'utf8',
'wd': word,
}
# 拼接url
query_string = urllib.parse.urlencode(data)
url += query_string
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
# 构建请求对象
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
filename = word + '.html'
# 写入到文件中
with open(filename, 'wb') as fp:
fp.write(response.read())
# 插件,sublime-repl
post请求
- 百度翻译 XMLHttpRequest
import urllib.request
import urllib.parse
post_url = 'http://fanyi.baidu.com/sug'
# word = input('请输入要查询的英文单词:')
word = 'baby'
# post携带的参数
data = {
'kw': word
}
# 对post参数进行处理
data = urllib.parse.urlencode(data).encode('utf8')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
# 构建请求对象
request = urllib.request.Request(url=post_url, headers=headers)
# 发送请求
response = urllib.request.urlopen(request, data=data)
print(response.read().decode('utf8'))
进阶
import urllib.request
import urllib.parse
post_url = 'http://fanyi.baidu.com/v2transapi'
# post参数
formdata = {
'from': 'en',
'to': 'zh',
'query': 'baby',
'transtype': 'realtime',
'simple_means_flag': '3',
'sign': '814534.560887',
'token': '921cc5d0819e0f1b4212c7fdc3b23866',
}
# 处理表单数据
formdata = urllib.parse.urlencode(formdata).encode('utf8')
headers = {
'Accept': '*/*',
# 'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
# 'Content-Length': '121',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Cookie': 'BAIDUID=59CCBF5477FDC81799054A3DA85BEF88:FG=1; BIDUPSID=59CCBF5477FDC81799054A3DA85BEF88; PSTM=1529815427; pgv_pvi=2975680512; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; locale=zh; to_lang_often=%5B%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%2C%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%5D; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; from_lang_often=%5B%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%2C%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%5D; H_PS_PSSID=1436_26432_21094_20929; PSINO=2; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1531189205,1531202737; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1531202737',
'Host': 'fanyi.baidu.com',
'Origin': 'http://fanyi.baidu.com',
'Referer': 'http://fanyi.baidu.com/?aldtype=16047',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest',
}
# 构建请求对象,发送请求
request = urllib.request.Request(url=post_url, headers=headers)
# 发送请求
response = urllib.request.urlopen(request, data=formdata)
print(response.read().decode('utf-8'))
ajax-get请求
- 豆瓣电影排行榜
DOM BOM
https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&start=0&limit=20
start = 20 limit = 20
start = 40 limit = 20
第n页 start = (n-1)*20 limit = 20
import urllib.request
import urllib.parse
url = 'https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&'
print('每页显示20条数据')
# page = input('请输入要第几页数据:')
page = 3
# 根据page就可以计算出来start和limit的值
start = (page-1) * 20
limit = 20
# 写get参数
data = {
'start': start,
'limit': limit,
}
# 将字典转化为query_string格式
query_string = urllib.parse.urlencode(data)
# 拼接url
url += query_string
# print(url)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
print(response.read().decode('utf8'))
ajax-post请求
- 肯德基店铺位置
import urllib.request
import urllib.parse
post_url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname'
data = {
'cname': '石河子',
'pid': '',
'pageIndex': '1',
'pageSize': '10',
}
# 处理data数据
data = urllib.parse.urlencode(data).encode('utf8')
#
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
request = urllib.request.Request(url=post_url, headers=headers)
response = urllib.request.urlopen(request, data=data)
print(response.read().decode())
2、复杂get
百度贴吧
-
https://tieba.baidu.com/f?kw=%E6%9D%8E%E6%AF%85&ie=utf-8&pn=0
第一页 pn=0
第二页 pn=50
第三页 pn=100
第n页 pn=(n-1)*50
输入吧名,输入要爬取的起始页码,输入要爬取的结束页码
在当前创建一个文件夹,文件夹名字就是吧名,在吧名文件夹里面,有好多文件,就每一页的内容,第1页.html 第2页.html xxxx
import urllib.request
import urllib.parse
import os
import time
def handle_request(url, baname, page):
pn = (page-1) * 50
# 拼接url
data = {
'ie': 'utf8',
'kw': baname,
'pn': pn,
}
query_string = urllib.parse.urlencode(data)
url += query_string
# print(url)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
request = urllib.request.Request(url=url, headers=headers)
return request
# 根据请求对象,下载指定的响应内容到本地
def download(request, baname, page):
# 根据吧名,创建文件夹的过程
# 判断文件夹是否存在,不存在才创建
if not os.path.exists(baname):
os.mkdir(baname)
# 文件的名字
filename = '第%s页.html' % page
print('正在下载%s......' % filename)
# 拼接文件的全路径
filepath = os.path.join(baname, filename)
response = urllib.request.urlopen(request)
# 将内容都进来
content = response.read()
# 将内容写入到文件中
with open(filepath, 'wb') as fp:
fp.write(content)
print('结束下载%s' % filename)
def main():
url = 'https://tieba.baidu.com/f?'
# 要爬取的贴吧的名字
baname = input('请输入要爬取的吧名:')
# 起始页码、结束页码
start_page = int(input('请输入起始页码:'))
end_page = int(input('请输入结束页码:'))
# 搞个循环
for page in range(start_page, end_page + 1):
# 根据不同的page生成不同哦url,然后生成不同的请求
request = handle_request(url, baname, page)
# 发送请求,获取响应
download(request, baname, page)
# 停顿一下
time.sleep(2)
if __name__ == '__main__':
main()
3、URLError\HTTPError
异常处理,NameError, 这两个异常在urllib.error里面
URLError:不存在的域名,电脑没有联网
www.goudan.comHTTPError: 404找不到页面
HTTPError是URLError的子类,如果多个except在进行捕获,HTTPError要写到前面
import urllib.request
import urllib.error
'''
url = 'http://www.maodan.com/'
try:
response = urllib.request.urlopen(url)
except Exception as e:
print(e)
'''
url = 'https://blog.csdn.net/hudeyu777/article/details/76021574'
try:
response = urllib.request.urlopen(url)
except urllib.error.HTTPError as e:
print(e)
except urllib.error.URLError as e:
print(e)
4、Handler处理器、自定义Opener
是什麽意思呢?
++++引入请求对象为了定制头部
更高级的功能就要使用handler和opener,步骤都是首先创建一个handler,然后创建一个opener,然后通过opener的open方法来发送请求
通过handler和opener实现最基本的请求
import urllib.request
url = 'http://www.baidu.com/'
# 创建一个handler
handler = urllib.request.HTTPHandler()
# 根据handler来创建一个opener
opener = urllib.request.build_opener(handler)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
request = urllib.request.Request(url=url, headers=headers)
# 再往下发送请求操作,都通过opener的open方法,没有urlopen()这个方法了
response = opener.open(request)
print(response.read().decode('utf8'))
5、代理
- 代理:代驾,代购,代练
我们的代理
代理服务器:
--------------------西祠代理
--------------------快代理
- 浏览器配置代理
101.236.21.22:8866
程序配置代理
import urllib.request
url = 'http://www.baidu.com/s?ie=UTF-8&wd=ip'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
request = urllib.request.Request(url, headers=headers)
# 101.236.21.22:8866
# 使用代理访问
handler = urllib.request.ProxyHandler(proxies={'http': '101.236.21.22:8866'})
# 根据handler创建opener
opener = urllib.request.build_opener(handler)
response = opener.open(request)
with open('daili.html', 'wb') as fp:
fp.write(response.read())
6、cookie
cookie是什麽?会员卡
为什么引入cookie?http有一个特点,无状态
每次的请求和响应都是独立的,没有什么关系
缺点?
两次请求如果有联系,服务端识别不出来
要想识别出来,引入了cookie
day03-爬虫3
1、cookie
常用在模拟登录中
通过代码如何访问登录后的页面?
http://www.renren.com/960481378/profile
- (1)伪造cookie,进行访问
- (2)模拟浏览器的方式,首先发送post,先登录成功,然后发送get,访问登录后的页面
ps:fiddler上面,一个本上面有个箭头,这个就是post请求
import urllib.request
import urllib.parse
# 直接访问这个页面,肯定是不行的,需要伪造cookie
url = 'http://www.renren.com/960481378/profile'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
with open('renren.html', 'wb') as fp:
fp.write(response.read())
# 通过抓包,抓取浏览器访问时候的cookie,通过代码模拟发送即可
url = 'http://www.renren.com/960481378/profile'
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Cookie': 'anonymid=jjgfrq7uasf0qc; depovince=GW; jebecookies=49de5f46-0b80-490f-9277-f19688fc91a3|||||; _r01_=1; JSESSIONID=abcYl2ngsw4FBhTX16gsw; ick_login=52175eb0-6f30-4f87-b614-91fa81350f73; _de=F872F5698F7602B30ADE65415FC01940; p=42e7d8c2c4c06f39f70b2be38468f15f8; first_login_flag=1; ln_uact=17701256561; ln_hurl=http://head.xiaonei.com/photos/0/0/men_main.gif; t=d34342f75a43ffa1c061400e9126ea118; societyguester=d34342f75a43ffa1c061400e9126ea118; id=960481378; xnsid=79143e47; ver=7.0; loginfrom=null; wp_fold=0; jebe_key=2f649712-9cf8-44a8-8c6a-145cfab423ae%7C86ba94a3b75a9848502e25ac92562959%7C1531272254868%7C1',
'Host': 'www.renren.com',
'Referer': 'http://www.renren.com/960481378/profile',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
with open('renren.html', 'wb') as fp:
fp.write(response.read())
模拟登陆人人网获取登陆后的界面
import urllib.request
import urllib.parse
import http.cookiejar
# 高级请求cookie的代码实现
# 这个ck对象可以保存cookie
ck = http.cookiejar.CookieJar()
# 根据ck对象创建一个handler
handler = urllib.request.HTTPCookieProcessor(ck)
# 根据handler创建一个opener
opener = urllib.request.build_opener(handler)
# 往下所有的请求都使用opener.open()方法进行发送,这样就会和浏览器的功能一模一样,就是保存cookie并且自动携带cookie发送请求的功能
# 抓包获取到post_url
post_url = 'http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=201863950868'
# 表单数据
data = {
'email': '17701256561',
'icode': '',
'origURL': 'http://www.renren.com/home',
'domain': 'renren.com',
'key_id': '1',
'captcha_type': 'web_login',
'password': '776f67653bd0b50d6159b7b5173b74249b9e0765da701ff559c986054b9871a7',
'rkey': 'fe41ddb7ec32db83d8bdbcc6945e267a',
'f': 'http%3A%2F%2Fwww.renren.com%2F960481378%2Fprofile',
}
data = urllib.parse.urlencode(data).encode('utf8')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
request = urllib.request.Request(url=post_url, headers=headers)
response = opener.open(request, data=data)
print(response.read().decode('utf8'))
# with open('renren.html', 'wb') as fp:
# fp.write(response.read())
# 访问登录后的页面
url = 'http://www.renren.com/960481378/profile'
headers1 = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
request = urllib.request.Request(url=url, headers=headers1)
response = opener.open(request)
with open('renren.html', 'wb') as fp:
fp.write(response.read())
2、正则表达式
为什么引入正则表达式?
字符串,处理函数也非常重要,比如如果要去一个文件查找所有的邮箱
test@163.com
889910@qq.com正则表达式。可以匹配一类的东西,肯定有好多规则
世界上最难懂四种东西。女人的心,医生的处方、道士的符、程序媛的正则
正则表达式规则
1.单字符匹配:
. : 除了换行符以外所有的字符
\d : 匹配 0-9 [0-9] [1-5]
\D: 除了\d
\w : 数字、字母、下划线 [a-zA-Z0-9_]
\W : 除了\w
\s : 所有的空白字符,tab、空格 。。。
\S : 除了\s
[aeiou] : 匹配中括号里面任意个
2.数量修饰:
{5} : 修饰前面的字符出现5次
{5,8} : 5-8次
a{5,8}
aaaaaaaa 贪婪的
{5,} : 最少是5次
{0,} : 任意多次 *
{1,} : 至少1次 +
{0,1} : 可有可无 ?
python模块 re
re.match(): >>>> 从头开始匹配,匹配成功就返回
re.search() >>>> 从任意位置开始匹配,匹配成功返回 只找一个
返回的是对象, >>>> obj.group()
re.findall() :>>>>>查找所有符合要求的字符串
返回的是列表,列表里面都是匹配成功的字符串
3.边界:
^ : 以xxx开头
$ : 以xxx结尾
4.分组:
a(bc){5}
小括号作用
- (1)视为一个整体
- (2)子模式
贪婪模式:
.*
.+
.*? 取消贪婪
.+? 取消贪婪
模式修正
re.I : 忽略大小写
re.M : 视为多行模式
re.S : 视为单行模式
import re
# 创建正则对象
pattern = re.compile(r'\d{3,5}')
string = '我以前总共谈过1234567次恋爱,我总共结婚7654321次'
# ret = pattern.match(string)
# ret = pattern.search(string)
ret = pattern.findall(string)
print(ret)
pattern = re.compile(r'a(bc){3}')
string = '哈哈呵呵abcbcbcbcbc嘻嘻嘿嘿么么'
ret = pattern.search(string)
print(ret.group())
string = '哈哈<div><span>醉卧沙场君莫笑,古来征战几人回</span></div>嘻嘻'
pattern = re.compile(r'<(\w+)><(\w+)>.+</\2></\1>')
ret = pattern.search(string)
print(ret.group())
string = '<div>两岸猿声啼不住,轻舟已过万重山</div></div></div>'
pattern = re.compile(r'<div>.*?</div>')
ret = pattern.search(string)
print(ret.group())
"""
string = '''english
love is a forever problem
love is feel
'''
pattern = re.compile(r'^love', re.M)
ret = pattern.search(string)
print(ret.group())
"""
"""
string = '''<div>细思极恐
你的对手在看书
你的敌人在磨刀
你的闺蜜在减肥
隔壁老王在练腰
</div>
'''
pattern = re.compile(r'<div>.*</div>', re.S)
ret = pattern.search(string)
print(ret.group())
"""
def fn(ret):
number = int(ret.group())
number += 1
return str(number)
# 正则替换
string = '男人都喜欢19岁的女孩'
pattern = re.compile(r'\d+')
# ret = pattern.sub('40', string)
ret = pattern.sub(fn, string)
print(ret)
糗事百科图片--实战
import urllib.request
import urllib.parse
import re
import os
import time
def handle_request(url, page):
url += str(page) + '/'
# print(url)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
request = urllib.request.Request(url=url, headers=headers)
return request
def parse_content(request):
response = urllib.request.urlopen(request)
content = response.read().decode('utf8')
with open('lala.html', 'w', encoding='utf8') as fp:
fp.write(content)
# 解析内容,提取这一页所有的图片链接
pattern = re.compile(r'<div class="thumb">.*?<a href=".*?" target="_blank">.*?<img src="(.*?)" alt="(.*?)" />.*?</div>', re.S)
ret = pattern.findall(content)
# print(ret)
# exit()
# print(len(ret))
download(ret)
def download(ret):
for image_info in ret:
# 取出图片的链接
image_src = image_info[0]
# 取出图片的标题
image_alt = image_info[1]
# 拼接完整的url
image_src = 'https:' + image_src
dirname = 'tupian'
# 创建文件夹
if not os.path.exists(dirname):
os.mkdir(dirname)
# 得到后缀名
suffix = image_src.split('.')[-1]
# 文件名字
filename = image_alt + '.' + suffix
filepath = os.path.join(dirname, filename)
print('正在下载%s......' % filename)
# 下载图片
urllib.request.urlretrieve(image_src, filepath)
print('结束下载%s' % filename)
time.sleep(3)
def main():
start_page = int(input('请输入要爬取的起始页码:'))
end_page = int(input('请输入要爬取的结束页码:'))
url = 'https://www.qiushibaike.com/pic/page/'
for page in range(start_page, end_page + 1):
print('正在下载第%s页......' % page)
# 拼接url。构建请求对象
request = handle_request(url, page)
# 发送请求,获取响应,并且解析内容
parse_content(request)
print('结束下载第%s页' % page)
time.sleep(3)
if __name__ == '__main__':
main()
励志语句--实战
import urllib.request
import urllib.parse
import re
import time
def main():
url = 'http://www.yikexun.cn/lizhi/qianming/list_50_{}.html'
start_page = int(input('请输入起始页码:'))
end_page = int(input('请输入结束页码:'))
# 在这里打开文件
fp = open('lizhi.html', 'w', encoding='utf8')
for page in range(start_page, end_page + 1):
print('正在爬取第%s页......' % page)
# 拼接url,生成请求对象
request = handle_request(url, page)
content = urllib.request.urlopen(request).read().decode('utf8')
# 解析内容函数
parse_content(content, fp)
print('结束爬取第%s页' % page)
time.sleep(2)
fp.close()
def handle_request(url, page=None):
if page:
url = url.format(page)
# print(url)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
return urllib.request.Request(url=url, headers=headers)
def parse_content(content, fp):
# 写正则表达式,提取所有的标题和链接
pattern = re.compile(r'<h3><a href="(/lizhi/qianming/\d+\.html)">(.*?)</a></h3>')
ret = pattern.findall(content)
# print(ret)
# print(len(ret))
i = 0
for href_title in ret:
# 取出链接
href = 'http://www.yikexun.cn' + href_title[0]
# 取出标题
title = href_title[1]
print('正在爬取%s......' % title)
# 因为只有第一个title两边有b标签,所以这里写了一个小小的判断,只在循环第一次执行
if i == 0:
# 将title两边的b标签干掉
title = title.strip('<>b/')
i += 1
# print(title)
# 获取内容的函数
text = get_text(href)
# 将标题和内容写入到文件中
string = '<h1>%s</h1>%s' % (title, text)
fp.write(string)
print('结束爬取%s...' % title)
time.sleep(2)
def get_text(href):
# 构建请求对象,发送请求,解析响应,返回解析后的内容
request = handle_request(href)
content = urllib.request.urlopen(request).read().decode('utf8')
pattern = re.compile(r'<div class="neirong">(.*?)</div>', re.S)
ret = pattern.search(content)
# print(ret.group(1))
# exit()
text = ret.group(1)
# 将图片链接去除掉
pattern = re.compile(r'<img .*?>')
text = pattern.sub('', text)
return text
if __name__ == '__main__':
main()
day04-爬虫4
1、bs4语法
是什麽?
BeautifulSoup,就是一个第三方的库,使用之前需要安装
pip install bs4
pip进行安装,默认是从国外安装,所以需要将pip源设置为国内源,国内有豆瓣源、阿里源、网易源等等xxx
配置永久国内源:
- 1.windows配置方式:
(1)打开文件资源管理器
--------在地址栏中输入 %appdata%
(2)手动创建一个文件夹叫做 pip
(3)在pip的文件夹里面新建一个文件 pip.ini
(4)文件中写如下内容[global] index-url = http://pypi.douban.com/simple [install] trusted-host=pypi.douban.com
- linux配置方式:
(1)cd ~
(2)mkdir .pip
(3)vi ~/.pip/pip.conf
(4)编辑内容和windows的内容一模一样
pip install bs4 pip install lxml
bs4是什麽?
- 它的作用是能够快速方便简单的提取网页中指定的内容,给我一个网页字符串,然后使用它的接口将网页字符串生成一个对象,然后通过这个对象的方法来提取数据
lxml是什麽?
- lxml是一个解析器,也是下面的xpath要用到的库,bs4将网页字符串生成对象的时候需要用到解析器,就用lxml,或者使用官方自带的解析器 html.parser
bs4语法学习
- 通过本地文件进行学习,通过网络进行写代码
(1)根据标签名进行获取节点
只能找到第一个符合要求的节点
(2)获取文本内容和属性 - 属性
soup.a.attrs 返回一字典,里面是所有属性和值
soup.a['href'] 获取href属性
- 文本
soup.a.string
soup.a.text
soup.a.get_text()
【注】当标签里面还有标签的时候,string获取的为None,其他两个获取纯文本内容
(3)find方法
soup.find('a')
soup.find('a', class_='xxx')
soup.find('a', title='xxx')
soup.find('a', id='xxx')
soup.find('a', id=re.compile(r'xxx'))
【注】find只能找到符合要求的第一个标签,他返回的是一个对象
(4)find_all
返回一个列表,列表里面是所有的符合要求的对象
soup.find_all('a')
soup.find_all('a', class_='wang')
soup.find_all('a', id=re.compile(r'xxx'))
soup.find_all('a', limit=2) 提取出前两个符合要求的a
(5)select
选择,选择器 css中
常用的选择器
标签选择器、id选择器、类选择器
层级选择器**
div h1 a 后面的是前面的子节点即可
div > h1 > a 后面的必须是前面的直接子节点
属性选择器
input[name='hehe']
select('选择器的')
【注】返回的是一个列表,列表里面都是对象
【注】find find_all select不仅适用于soup对象,还适用于其他的子对象,如果调用子对象的select方法,那么就是从这个子对象里面去找符合这个选择器的标签
from bs4 import BeautifulSoup
# 生成soup对象
soup = BeautifulSoup(open('soup.html', encoding='utf8'), 'lxml')
print(type(soup))
print(soup.a.attrs)
print(soup.a.attrs['href'])
print(soup.a['href'])
print(soup.a.string)
print(soup.a.text)
print(soup.a.get_text())
print(soup.div.string)
print(soup.div.text)
print(soup.div.get_text())
ret = soup.find('a')
ret = soup.find('a', title='清明')
ret = soup.find('a', class_='dumu')
print(ret)
import re
ret = soup.find('a', id='xiaoge')
ret = soup.find('a', id=re.compile(r'^x'))
print(ret.string)
ret = soup.find_all('a')
print(ret[1].string)
ret = soup.find_all('a', class_='wang')
print(ret)
ret = soup.find_all('a', id=re.compile(r'^x'))
print(ret)
ret = soup.select('a')
ret = soup.select('#muxiong')
print(ret[0]['title'])
ret = soup.select('.wang')
print(ret)
ret = soup.select('div > a')
print(ret)
ret = soup.select('a[title=东坡肉]')
print(ret)
odiv = soup.select('.tang')[0]
ret = odiv.select('a')
print(ret)
滚滚长江东逝水,浪花淘尽英雄,是非成败转头看,青山依旧在,几度夕阳红
白发渔樵江渚上,观看秋月春风,一壶浊酒喜相逢,古今多少事,都付笑谈中
三国演义--实战
import urllib.request
import urllib.parse
from bs4 import BeautifulSoup
import time
# 给我一个url,返回一个请求对象
def handle_request(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
return urllib.request.Request(url=url, headers=headers)
def parse_first_page(url):
request = handle_request(url)
# 发送请求,获取响应
content = urllib.request.urlopen(request).read().decode('utf8')
time.sleep(2)
# 使用bs解析内容,生成soup对象
soup = BeautifulSoup(content, 'lxml')
# print(soup)
# 查找所有的章节链接对象
a_list = soup.select('.book-mulu > ul > li > a')
# print(ret)
# print(len(ret))
# 打开文件
fp = open('三国演义.txt', 'w', encoding='utf8')
# 遍历所有的a对象
for oa in a_list:
# 取出这个a的内容
title = oa.string
# 取出a的href属性
href = 'http://www.shicimingju.com' + oa['href']
print('正在下载%s......' % title)
# 向href发送请求,直接获取到解析之后的内容
neirong = get_neirong(href)
print('结束下载%s' % title)
time.sleep(2)
string = '%s\n%s' % (title, neirong)
# 将string写入到文件中
fp.write(string)
fp.close()
def get_neirong(href):
# 向href发送请求,获取响应,解析响应,返回内容
request = handle_request(href)
content = urllib.request.urlopen(request).read().decode('utf8')
# 生成soup对象,提取章节内容
soup = BeautifulSoup(content, 'lxml')
# 找包含章节内容的div
odiv = soup.find('div', class_='chapter_content')
neirong = odiv.text
return neirong
def main():
url = 'http://www.shicimingju.com/book/sanguoyanyi.html'
# 解析第一个页面,返回所有的章节列表
chapter_list = parse_first_page(url)
if __name__ == '__main__':
main()
智联招聘--实战
import urllib.request
import urllib.parse
import time
from bs4 import BeautifulSoup
class ZhiLianSpider(object):
def __init__(self, jl, kw, start_page, end_page):
# 保存到成员属性中,这样在其他的方法中就可以直接使用
self.jl = jl
self.kw = kw
self.start_page = start_page
self.end_page = end_page
self.items = []
def handle_request(self, page):
url = 'https://sou.zhaopin.com/jobs/searchresult.ashx?'
data = {
'jl': self.jl,
'kw': self.kw,
'p': page,
}
query_string = urllib.parse.urlencode(data)
# 拼接url
url += query_string
# print(url)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
return urllib.request.Request(url=url, headers=headers)
def parse_content(self, content):
# 生成soup对象
soup = BeautifulSoup(content, 'lxml')
# 首先找到所有的table
table_list = soup.find_all('table', class_='newlist')[1:]
# print(table_list)
# print(len(table_list))
# 遍历所有的table,依次提取每一个工作的信息
for table in table_list:
# 职位名称
zwmc = table.select('.zwmc a')[0].text.strip('\xa0')
# 公司名称
gsmc = table.select('.gsmc a')[0].text
# 职位月薪
zwyx = table.select('.zwyx')[0].text
# 工作地点
gzdd = table.select('.gzdd')[0].text
# print(gzdd)
# exit()
item = {
'职位名称': zwmc,
'公司名称': gsmc,
'职位月薪': zwyx,
'工作地点': gzdd,
}
self.items.append(item)
def run(self):
# 搞个循环
for page in range(self.start_page, self.end_page + 1):
print('正在爬取第%s页......' % page)
# 拼接url的过程,构建请求对象
request = self.handle_request(page)
content = urllib.request.urlopen(request).read().decode('utf8')
# 给我请求对象,解析并且提取内容
self.parse_content(content)
print('结束爬取第%s页' % page)
time.sleep(2)
# 将所有的工作保存到文件中
string = str(self.items)
with open('work.txt', 'w', encoding='utf8') as fp:
fp.write(string)
def main():
# 输入工作地点
jl = input('请输入工作地点:')
# 输入工作关键字
kw = input('请输入关键字:')
# 输入起始页码
start_page = int(input('请输入起始页码:'))
# 输入结束页码
end_page = int(input('请输入结束页码:'))
zhilian = ZhiLianSpider(jl, kw, start_page, end_page)
zhilian.run()
if __name__ == '__main__':
main()
2、xpath语法
xml : 和json是一样的,用在数据交互和传输中,但是到现在用的基本上都是json格式
为什么使用json格式?因为js原生支持
xpath : 就是用来解析xml数据的
因为这个xml和html很像,所以在python里面有一个第三方库 lxml,提供了一些xpath可以解析html的一些函数,你可以直接使用
pip install lxml
xpath简单语法:
/ ----------根节点开始查找
// --------从任意位置开始查找
. ---------从当前节点开始查找
.. -----从上一级节点开始查找
@ -----------选取属性
bookstore/book :查找bookstore这个节点下面所有的book,直接子节点
//book : 在整个文档中查找所有的book
bookstore//book :查找bookstore下面所有的book
//@lang :-查找所有拥有lang属性的节点
bookstore/book[1] :查找第一个book,下标从1开始
bookstore/book[last()] :-最后一个book
bookstore/book[last()-1] : 倒数第二个
//title[@lang] :所有的有lang属性的title
//title[@lang='eng'] :所有的lang属性的值为eng的title节点
day05-爬虫5
1、xpath使用
安装xpath插件
- 启动和关闭插件: ctrl + shift + x
常用的xpath
(1)属性定位
- 查找id=kw的input框
//input[@id="kw"]
(2)层级和索引定位
//div[@id="head"]/div/div[3]/a[2]
(3)模糊匹配
contains
class属性包含av的所有a
//a[contains(@class,"av")]
内容包含产品的所有a
//a[contains(text(),"产品")]
starts_with
starts-with
class以m开头的所有a
//a[starts-with(@class,"m")]
内容以新开头的所有a
//a[starts-with(text(),"新")]
(4)取文本和属性
- 找内容以新开头的所有a标签的文本内容
//a[starts-with(text(),"新")]/text()
- 找内容以新开头的所有a标签的href属性
//a[starts-with(text(),"新")]/@href
代码中操作xpath
步骤:给一个网页字符串,会将网页字符串生成对象,然后根据对象的xpath方法去找指定的节点即可
from lxml import etree
# 将本地的文件生成tree对象
tree = etree.parse('xpath.html')
# ret = tree.xpath('//li[@id="fei"]')
# ret = tree.xpath('//div[@class="mingju"]/ul/li[2]/a')
# ret = tree.xpath('//li[contains(text(),"花")]')
# ret = tree.xpath('//a[starts-with(@class,"y")]/text()')
# ret = tree.xpath('//a[starts-with(@class,"y")]/@href')
ret = tree.xpath('//div[@id="xing"]//text()')
string = '-'.join(ret).replace('\n', '').replace('\t', '')
print(string)
本地测试
【注】获取标签里面还有标签的内容的时候
obj/text() 只能找到本标签里面的内容,返回的都是列表,列表需要自己处理
obj//text() 标签里面所有的内容,返回的是列表,列表需要处理
下载图片例子
sc.chinaz.com
http://sc.chinaz.com/tag_tupian/YaZhouMeiNv.html 第一页
http://sc.chinaz.com/tag_tupian/yazhoumeinv_2.html 第二页
from lxml import etree
import time
import urllib.request
import urllib.parse
import os
def handle_request(url, url_er, page):
if page == 1:
url = url
else:
url = url_er.format(page)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
return urllib.request.Request(url=url, headers=headers)
def parse_content(content):
# 生成tree对象
tree = etree.HTML(content)
# 写xpath,来获取所有的图片的image_src
image_src_list = tree.xpath('//div[@id="container"]//img/@src2')
image_alt_list = tree.xpath('//div[@id="container"]//img/@alt')
# print(image_src_list)
# print(len(image_src_list))
for image_src in image_src_list:
dirname = 'meinv'
if not os.path.exists(dirname):
os.mkdir(dirname)
filename = image_alt_list[image_src_list.index(image_src)]
suffix = image_src.split('.')[-1]
filename = filename + '.' + suffix
filepath = os.path.join(dirname, filename)
print('正在下载%s。。。。。。' % filename)
urllib.request.urlretrieve(image_src, filepath)
print('结束下载%s' % filename)
time.sleep(2)
def main():
start_page = int(input('请输入起始页码:'))
end_page = int(input('请输入结束页码:'))
url = 'http://sc.chinaz.com/tag_tupian/YaZhouMeiNv.html'
url_er = 'http://sc.chinaz.com/tag_tupian/yazhoumeinv_{}.html'
for page in range(start_page, end_page + 1):
print('正在下载第%s页。。。。。。' % page)
request = handle_request(url, url_er, page)
content = urllib.request.urlopen(request).read().decode('utf8')
# 解析内容函数
parse_content(content)
print('结束下载第%s页' % page)
time.sleep(2)
if __name__ == '__main__':
main()
懒加载技术
只显示可视区内的图片,不再可视区内的不显示,等用户滚动滚动条,图片呈现在可视区的时候在显示
如何实现的?
<img src='xxx'>
<img src2='xxx'> => 出现在可视区,js动态修改为 <img src='xxx'>
data-original=xxx
class='lazy'
data-src='xxx'
json处理
- 获取豆瓣电影的json数据写入txt
import json
import urllib.request
import urllib.parse
url = 'https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&start=20&limit=20'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
request = urllib.request.Request(url=url, headers=headers)
content = urllib.request.urlopen(request).read().decode('utf8')
# print(content)
# 将json格式数据转化为python对象
obj = json.loads(content)
# print(type(obj))
# 现在的obj是一个列表,列表里面都是字典
lt = []
# 遍历列表,依次提取每一个字典里面的电影的名字和评分
for movie in obj:
title = movie['title']
score = movie['score']
# movie['xixi']['hehe']['haha'][0]['lala']
image_url = movie['cover_url']
item = {
'电影名字': title,
'电影评分': score,
'电影海报': image_url
}
lt.append(item)
string = json.dumps(lt, ensure_ascii=False)
with open('movie1.txt', 'w', encoding='utf8') as fp:
fp.write(string)
2、jsonpath
jsonpath是用来解析json数据的
格式: {} [] "" , :
python如何解析json格式
(1)自带数据类型进行解析
import json
json.dumps() : 将python对象转化为json格式字符串
ensure_ascii=False 写入文件中文显示
json.loads() : 将json格式字符串转化为python对象
(2)jsonpath解析(了解)
pip install jsonpath
http://blog.csdn.net/luxideyao/article/details/77802389
/ $ 根元素
. @ 当前元素
/ . 层级分隔符
// .. 任意位置查找
- jsonpath使用规则
import json
import jsonpath
fp = open('book.json', 'r', encoding='utf8')
string = fp.read()
fp.close()
# 将json格式字符串转化为python对象
obj = json.loads(string)
# 使用jsonpath
# 查找所有book的author节点内容
# ret = jsonpath.jsonpath(obj, '$.store.book[*].author')
# 从根下面开始找所有的author
ret = jsonpath.jsonpath(obj, '$..author')
# 查找store下面的所有节点
ret = jsonpath.jsonpath(obj, '$.store.*')
# 查找store下面的所有price节点
ret = jsonpath.jsonpath(obj, '$.store..price')
# 查找第三本book
ret = jsonpath.jsonpath(obj, '$..book[2]')
# 查找最后一本书籍
ret = jsonpath.jsonpath(obj, '$..book[(@.length-1)]')
print(ret)
淘宝评论---实战
用户头像,用户昵称,评论内容,评论图片,评论时间,手机信息
https://rate.taobao.com/feedRateList.htm?auctionNumId=559141739630&userNumId=100340983¤tPageNum=3&pageSize=20
import json
import jsonpath
import urllib.request
import urllib.parse
import time
# 用来存放所有的评论信息
items = []
def handle_request(page, url):
url = url.format(page)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
return urllib.request.Request(url=url, headers=headers)
def parse_content(content):
# 如何解析content
# 处理content,将其转化为合法的json格式字符串
content = content.strip('() \n\t\r')
# print(content)
obj = json.loads(content)
# print(type(obj))
# 提取出来所有的评论
comments_list = obj['comments']
# 遍历列表,依次提取每一条评论的你要的信息
for comment in comments_list:
# 用户头像
# avatar = comment['user']['avatar']
avatar = jsonpath.jsonpath(comment, '$..avatar')[0]
# 用户昵称
nick = comment['user']['nick']
# 评论内容
neirong = comment['content']
# 图片
photos_list = comment['photos']
photos = []
for photo in photos_list:
# 获取小图
little_image = photo['thumbnail']
# 获取大图
big_image = photo['url']
photos.append((little_image, big_image))
# 时间
ping_time = comment['date']
# 手机信息
info = jsonpath.jsonpath(comment, '$..sku')[0]
item = {
'用户头像': avatar,
'用户昵称': nick,
'评论内容': neirong,
'晒图': photos,
'评论时间': ping_time,
'手机信息': info,
}
items.append(item)
def main():
url = 'https://rate.taobao.com/feedRateList.htm?auctionNumId=559141739630&userNumId=100340983¤tPageNum={}&pageSize=20'
start_page = int(input('请输入起始页码:'))
end_page = int(input('请输入结束页码:'))
for page in range(start_page, end_page + 1):
# for page in range(1, 2):
print('正在爬取第%s页......' % page)
request = handle_request(page, url)
content = urllib.request.urlopen(request).read().decode('utf8')
parse_content(content)
print('结束爬取第%s页......' % page)
time.sleep(2)
# 爬取完毕, 写入到文件中
string = json.dumps(items, ensure_ascii=False)
with open('taobao.json', 'w', encoding='utf8') as fp:
fp.write(string)
if __name__ == '__main__':
main()
3、selenium+phantomjs
http://chromedriver.storage.googleapis.com/index.html
http://blog.csdn.net/huilan_same/article/details/51896672
day06-爬虫6
1、selenium+phantomjs
selenium是什麽?
是一个浏览器自动化测试工具,自动化就是通过代码操作浏览器,让浏览器自动的做一些操作,是python的第三方库,需要安装才能使用
pip install selenium
谷歌驱动下载地址
http://chromedriver.storage.googleapis.com/index.html
驱动和浏览器版本关系映射表
http://blog.csdn.net/huilan_same/article/details/51896672
selenium 操作谷歌浏览器
需要有一个谷歌浏览器的驱动,然后操作这个驱动
from selenium import webdriver
import time
# 根据webdriver里面的类去创建一个谷歌浏览器对象
path = r'C:\Users\ZBLi\Desktop\1805\day06\ziliao\chromedriver.exe'
browser = webdriver.Chrome(path)
# 再往下,操作浏览器就是操作对象
# 打开百度
url = 'http://www.baidu.com/'
browser.get(url)
time.sleep(2)
# 查找百度输入框
my_input = browser.find_element_by_id('kw')
# 写内容
my_input.send_keys('清纯美女')
time.sleep(2)
# 查找按钮框
button = browser.find_element_by_id('su')
button.click()
time.sleep(3)
# 查找阳光美女链接
href = browser.find_elements_by_xpath('//div[@class="op-img-covers-divide-high"][2]/a[2]')[0]
href.click()
time.sleep(3)
# 推出浏览器
browser.quit()
find_element_by_id 根据id找固定的节点
find_elements_by_name 根据name找节点
find_elements_by_xpath 根据xpath查找
find_elements_by_tag_name 根据标签名查找
find_elements_by_class_name 根据类名查找
find_elements_by_css_selector 根据选择器查找
find_elements_by_link_text 根据链接内容查找
作用在哪?
让selenium操作的是phantomjs
phantomjs是什麽?
就是一款浏览器,无界面的浏览器,具有浏览器的功能,html\css\js这些文件浏览器可以直接执行。
我们关注的就是phantomjs可以执行js文件的功能,在爬取网站的时候,经常碰见动态加载数据的过程。js DOM,解决方法两种
(1)抓接口,拿出来接口发送请求,解析即可
(2)让浏览器直接执行js,然后我要执行js之后的网页代码,selenium+phantomjs,终极解决方案,大招,缺点:发请求之后要有停顿
操作phantomjs
from selenium import webdriver
import time
path = r'C:\Users\ZBLi\Desktop\1805\day06\ziliao\phantomjs-2.1.1-windows\bin\phantomjs.exe'
browser = webdriver.PhantomJS(path)
browser.get('http://www.baidu.com/')
time.sleep(3)
# 拍照片,记录走到哪了
browser.save_screenshot(r'tupian\baidu1.png')
browser.find_element_by_id('kw').send_keys('美女')
time.sleep(2)
browser.find_element_by_id('su').click()
time.sleep(3)
browser.save_screenshot(r'tupian\baidu2.png')
browser.quit()
代码中模拟滚动条滚动到底部
js = 'document.body.scrollTop=10000'
browser.execute_script(js)
from selenium import webdriver
import time
path = r'C:\Users\ZBLi\Desktop\1805\day06\ziliao\phantomjs-2.1.1-windows\bin\phantomjs.exe'
browser = webdriver.PhantomJS(path)
url = 'https://movie.douban.com/typerank?type_name=%E5%8A%A8%E4%BD%9C&type=5&interval_id=100:90&action='
browser.get(url)
time.sleep(3)
browser.save_screenshot(r'tupian\douban1.png')
# 执行一句js代码即可
# for x in range(1, 6):
# js = 'document.body.scrollTop=10000'
# browser.execute_script(js)
# time.sleep(3)
# browser.save_screenshot(r'tupian\douban2.png')
js = 'document.body.scrollTop=10000'
browser.execute_script(js)
time.sleep(3)
# 获取执行js之后的网页代码
with open(r'tupian\douban.html', 'w', encoding='utf8') as fp:
fp.write(browser.page_source)
# 往下就可以通过bs或者xpath来解析网页内容了
# 模拟点击加载更多,就是通过browser找到那个按钮,让按钮执行click方法即可
browser.quit()
获取页面的执行js之后的代码
browser.page_source
2、Headless Chrome
是什麽?
无头的谷歌浏览器-无界面的谷歌浏览器,phantomjs现在已经没人维护了,
要求:windows(60+以上)、linux(59+以上)
from selenium.webdriver.chrome.options import Options
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
from selenium import webdriver
import time
from selenium.webdriver.chrome.options import Options
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
path = r'C:\Users\ZBLi\Desktop\1805\day06\ziliao\chromedriver.exe'
browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)
browser.get('http://www.baidu.com/')
time.sleep(2)
browser.save_screenshot(r'tupian\baidu3.png')
browser.quit()
- 当然:不仅谷歌有无界面模式,火狐也有,ie也有,phantomjs
3、requests
是什麽?
- 是一个第三方库,这个库和urllib是一样的,就是模拟浏览器发送http请求的,requests是对urllib的一层封装,所以提供的接口更加的人性化
详请地址
http://docs.python-requests.org/zh_CN/latest/index.html
安装方式
pip install requests
get\带参数的get
data是一个参数字典
r = requests.get(url=url, params=data)
响应对象r
r.text 字符串格式内容
r.content 字节格式内容
r.headers 响应头部
r.url 请求url
r.status_code 状态码
import requests
'''
url = 'http://www.baidu.com/'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
r = requests.get(url=url, headers=headers)
'''
# r是响应对象
# 网页的字符串内容
# print(r.text)
# 字节内容
# print(r.content)
# 获取网页的url
# print(r.url)
# 获取响应头部
# print(r.headers)
# 获取状态码
# print(r.status_code)
url = 'https://www.baidu.com/s?'
data = {
'ie': 'utf8',
'wd': '周杰伦'
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
r = requests.get(url=url, params=data, headers=headers)
with open(r'tupian\zhou.html', 'wb') as fp:
fp.write(r.content)
post
必应翻译
data是参数字典
r = requests.post(url=url, data=data)
import requests
post_url = 'https://cn.bing.com/ttranslationlookup?&IG=5C360E60322D4FA4865EEBCF710B93B6&IID=translator.5036.2'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
fromdata = {
'from': 'zh-CHS',
'to': 'en',
'text': '皇上',
}
r = requests.post(url=post_url, data=fromdata, headers=headers)
print(r.text)
会话
登录,人人网---获取保存cooike
s = requests.Session()
s.post()
s.get()
import requests
# 使用会话技术,首先创建一个会话
# 往下所有操作,使用s进行发送 s.post s.get
s = requests.Session()
post_url = 'http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=2018621432232'
data = {
'email':'17701256561',
'icode':'',
'origURL':'http://www.renren.com/home',
'domain':'renren.com',
'key_id':'1',
'captcha_type':'web_login',
'password':'bd20fe8cf1541a10558676a6eeccb4a1a786cfc09823ddd69d5bbaafc7060292',
'rkey':'227f4ceb2f44827f9de8296ca1ef1c3f',
'f':'https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DaovDobnt13PO-vgvw1r-eSnSe_QNvNGtexiQFzyME-a%26wd%3D%26eqid%3Db5d58b1e000297f4000000025b4d88e3',
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
r = s.post(url=post_url, headers=headers, data=data)
# print(r.text)
url = 'http://www.renren.com/960481378/profile'
r = s.get(url, headers=headers)
with open('renren.html', 'wb') as fp:
fp.write(r.content)
公交爬取
import requests
from lxml import etree
import re
import json
import time
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
def parse_first_page(url):
r = requests.get(url=url, headers=headers)
# 生成一个tree对象
tree = etree.HTML(r.text)
# 通过tree对象查找所有的数字、字母链接
number_href_list = tree.xpath('//div[@class="bus_kt_r1"]/a/@href')
char_href_list = tree.xpath('//div[@class="bus_kt_r2"]/a/@href')
return number_href_list + char_href_list
def parse_second_page(url, all_href, fp):
# 为了拼接完整的url,先将右边的 / 干掉
url = url.rstrip('/')
for href in all_href:
href = url + href
r = requests.get(href, headers=headers)
tree = etree.HTML(r.text)
# 解析,获取所有的公交href信息
bus_href_list = tree.xpath('//div[@id="con_site_1"]/a/@href')
bus_name_list = tree.xpath('//div[@id="con_site_1"]/a/text()')
# print(bus_href_list)
# exit()
# 向列表中的url依次发送请求,解析内容
parse_third_page(url, bus_href_list, bus_name_list, fp)
def parse_third_page(url, bus_href_list, bus_name_list, fp):
for bus_href in bus_href_list:
title = bus_name_list[bus_href_list.index(bus_href)]
print('正在爬取%s......' % title)
# 拼接完整的url
bus_href = url + bus_href
# 向每一路公交的详情页发送请求
r = requests.get(url=bus_href, headers=headers)
# 在下面的函数中,解析每一路公交的详细信息
parse_content(r.text, fp)
print('结束爬取%s' % title)
time.sleep(1)
def parse_content(content, fp):
tree = etree.HTML(content)
# 获取线路名称
name = tree.xpath('//div[@class="bus_i_t1"]/h1/text()')[0]
# 获取运行时间
runtime = tree.xpath('//div[@class="bus_i_content"]/p[1]/text()')[0]
# 获取票价信息
price = tree.xpath('//div[@class="bus_i_content"]/p[2]/text()')[0]
# 公交公司
try:
company = tree.xpath('//div[@class="bus_i_content"]/p[3]/a/text()')[0]
except Exception as e:
company = ''
# 更新时间
gxsj = tree.xpath('//div[@class="bus_i_content"]/p[last()]/text()')[0]
# 获取公交路线长度
try:
length = tree.xpath('//div[@class="bus_label "]/p/text()')[0]
pattern = re.compile(r'\d+\.\d+')
ret = pattern.search(length)
length = ret.group()
except Exception as e:
length = ''
total_list = tree.xpath('//span[@class="bus_line_no"]/text()')
# 获取上行总站数, 使用正则将总站数拿走
pattern = re.compile(r'\d+')
up_total = total_list[0]
up_total = pattern.search(up_total).group()
# 获取下行总站数
try:
down_total = total_list[1]
down_total = pattern.search(down_total).group()
except Exception as e:
down_total = ''
# 获取上行的公交站牌信息
up_site_name = tree.xpath('//div[@class="bus_line_site "][1]//a/text()')
# 获取下行的公交站牌信息
try:
down_site_name = tree.xpath('//div[@class="bus_line_site "][2]//a/text()')
except Exception as e:
down_site_name = []
# 将公交的详细信息保存到字典中
item = {
'线路名称': name,
'运行时间': runtime,
'票价信息': price,
'公交公司': company,
'更新时间': gxsj,
'线路长度': length,
'上行站数': up_total,
'上行站牌': up_site_name,
'下行站数': down_total,
'下行站牌': down_site_name,
}
string = json.dumps(item, ensure_ascii=False)
fp.write(string + '\n')
def main():
# 打开文件
fp = open('北京公交路线.txt', 'w', encoding='utf8')
url = 'http://beijing.8684.cn/'
# 获取所有的数字、字母链接
all_href = parse_first_page(url)
# 遍历列表,依次发送请求,解析二级页面
parse_second_page(url, all_href, fp)
fp.close()
if __name__ == '__main__':
main()
4、登录---验证码
验证码:
(1)将验证码下载到本地,让用户手动输入
(2)使用软件识别,效率不高
(3)使用打码平台,识别率高
import requests
# 搞一个会话
s = requests.Session()
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
# 先将验证码下载到本地
get_url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
r = s.get(get_url, headers=headers)
# 需要向图片src发送请求,将验证码下载到本地
image_src = 'https://so.gushiwen.org/RandCode.ashx'
r = s.get(image_src, headers=headers)
with open('code.png', 'wb') as fp:
fp.write(r.content)
code = input('请输入验证码:')
post_url = 'https://so.gushiwen.org/user/login.aspx?from=http%3a%2f%2fso.gushiwen.org%2fuser%2fcollect.aspx'
data = {
'__VIEWSTATE': 'BvBAwAIKh29BShbC/yKMDsjiElxi+d4wdH3pR2dacgsifqK0rmUzL4Mc9YzHGDc6P6rqB4wMZ39uRj2MpaaSjQtarGnIo6qf1djLGa75XLo/S4b65Uhv2TETKt0=',
'__VIEWSTATEGENERATOR':'C93BE1AE',
'from': 'http://so.gushiwen.org/user/collect.aspx',
'email': '1090509990@qq.com',
'pwd': '123456',
'code': code,
'denglu': '登录',
}
r = s.post(post_url, headers=headers, data=data)
print(r.text)
多进程
多任务:生活中来看,就是多个任务同时进行,喝酒聊天,开车,手脚并用,唱歌跳舞
电脑中:录屏、sublime、vnc服务端、浏览器打开等
代码中:实现多任务,多进程、多线程
进程:电脑中,每一个软件启动起来都是一个进程,
代码中:没有运行的时候称之为程序,运行起来之后就是一个进程
多进程:进程只有一个,称之为主进程,子进程,要实现两个函数同时执行,就要通过主进程来创建子进程
操作系统实现,只是在进程之间来回切换,切换的非常快,看着像同时执行一样
如何实现?
面向过程:(process)
p = Process(target=xxx, name=xxx, args=(xxx,))
target: 进程启动之后要执行的函数
name: 给进程起个名字
args: 给子进程传递的参数,是一个元组
p.start() 启动进程
p.join() 让主进程等待子进程结束
os.getpid() 获取当前进程id号
os.getppid() 获取父进程的id号
from multiprocessing import Process
import time
import os
# 想让子进程1执行sing函数
def sing(a):
print('接受的参数为%s' % a)
# 进程id号
print('进程-%s-开始运行' % os.getpid())
print('父进程为%s' % os.getppid())
for x in range(1, 5):
print('我在唱小情歌')
time.sleep(1)
# 想让子进程2执行dance函数
def dance(a):
print('进程-%s-开始运行' % os.getpid())
print('父进程为%s' % os.getppid())
for x in range(1, 5):
print('我在跳钢管舞')
time.sleep(1)
def main():
# 主进程
print('主进程id号为%s' % os.getpid())
a = '青花瓷'
# 创建进程
p_sing = Process(target=sing, name='唱歌', args=(a,))
p_dance = Process(target=dance, name='跳舞', args=(a,))
# 启动进程
p_sing.start()
p_dance.start()
# 获取进程名字
print(p_sing.name)
print(p_dance.name)
# 因为主进程中有子进程的信息,所以主进程必须等子进程结束之后再结束
p_sing.join()
p_dance.join()
print('主进程结束')
if __name__ == '__main__':
main()
面向对象:
from multiprocessing import Process
import time
class SingProcess(Process):
def __init__(self, a):
# 如果要重写构造方法,一定得手动调用父类的构造方法
super().__init__()
self.a = a
def run(self):
print('传递的参数为%s' % self.a)
for x in range(1, 5):
print('我在唱小情歌')
time.sleep(1)
class DanceProcess(Process):
def run(self):
for x in range(1, 5):
print('我在跳钢管舞')
time.sleep(1)
def main():
a = '现在很多老歌手为什么不唱歌了'
p_sing = SingProcess(a)
# 启动进程,进程启动之后默认执行类里面的run方法
p_sing.start()
# p_dance = DanceProcess()
# p_dance.start()
p_sing.join()
# p_dance.join()
print('主进程结束')
if __name__ == '__main__':
main()
多进程拷贝,拷贝文件夹,假如文件夹里面只有文件。假如有100个文件,那么拷贝的时候先拷贝第一个,然后第二个,然后第三个====
拷贝一个文件就是一个任务,那一共100文件,那难道要开辟100个进程吗?
进程并不是越多越好,切换占用的时间越大
练习:多进程拷贝
拷贝之前记录时间戳,拷贝之后记录时间戳,计算拷贝的时间
多进程拷贝也是一样,哪个时间短
引入进程池,规定能创建几个进程,来了任务,5个进程
进程之间是否共享全局变量
全局变量
进程之间是否共享全局变量,不共享
每一个进程都是单独的代码
from multiprocessing import Process
import os
import time
count = 100
# 该进程用来修改全局变量的值
def change():
global count
count += 100
print('进程%s修改后的值为%s' % (os.getpid(), count))
# 该进程用来读取全局变量的值
def read():
print('进程%s读取的值为%s' % (os.getpid(), count))
def test(c):
a = 100
if c == 'hello':
a += 100
time.sleep(2)
print('进程%s修改a的值为%s' % (os.getpid(), a))
else:
time.sleep(5)
print('进程%s读取a的值为%s' % (os.getpid(), a))
def main():
'''
p1 = Process(target=change)
p1.start()
time.sleep(2)
p2 = Process(target=read)
p2.start()
'''
a = 'hello'
b = 'world'
p1 = Process(target=test, args=(a, ))
p2 = Process(target=test, args=(b, ))
p1.start()
p2.start()
p1.join()
p2.join()
if __name__ == '__main__':
main()
进程池
from multiprocessing import Process
from multiprocessing import Pool
import os
import time
def test(name):
print('任务%s正在运行,进程id号为%s' % (name, os.getpid()))
time.sleep(2)
def main():
# 创建一个进程池对象
po = Pool(3)
# 给进程池添加任务
lt = ['关羽', '赵云', '张飞', '马超', '黄忠', '吕布', '孙策', '大乔']
for name in lt:
po.apply_async(test, args=(name, ))
# 进程池使用完毕之后,关闭
po.close()
# 让主进程等待结束
po.join()
print('主进程、进程池全部结束')
if __name__ == '__main__':
main()
多线程
线程:比如qq。比如暴风影音,比如word
可以同时语音、同时视频、同时聊天,多线程
暴风影音,视频播放、音频播放,多线程
word,打字,拼写检查,等,多线程
多任务的实现:多进程、多线程
主进程-子进程1-子进程2
特点:进程之间没有关系,如果一个进程挂了,不影响其它子进程
进程-主线程-子线程1-子线程2
特点:线程之间有关系,如果一个线程挂了,整个进程就挂了
实现方式:(thread)
面向过程
t = Thread(target=xxx, name=xxx, args=(xxx,))
target: 线程启动之后要执行的函数
name: 线程的名字
args: 给线程传递的参数
t.start(): 启动线程
t.join(): 让主线程等待子线程结束
threading.current_thread().name : 获取线程名字
import threading
import time
def sing(song):
print('传递过来的参数为%s' % song)
print('获取线程的名字为%s' % threading.current_thread().name)
for x in range(1, 5):
print('我在唱老情歌')
time.sleep(1)
def dance():
for x in range(1, 5):
print('我在跳广场舞')
time.sleep(1)
def main():
a = '广岛之恋'
# 这是一个进程,进程里面有一个主线程,然后主线程创建子线程1(唱歌),子线程2(跳舞)
t_sing = threading.Thread(target=sing, name='唱歌', args=(a, ))
t_dance = threading.Thread(target=dance, name='跳舞')
# 启动线程
t_sing.start()
t_dance.start()
t_sing.join()
t_dance.join()
print('主线程、子线程同时结束')
if __name__ == '__main__':
main()
面向对象
import threading
import time
# 滕王阁序 王勃
# 命硬
# 沁园春-雪
# 出师表
class MyThread(threading.Thread):
def __init__(self, a):
super().__init__()
self.a = a
def run(self):
print('传递过来的参数为%s' % self.a)
for x in range(1, 5):
print('凤凰台上凤凰游,凤去台空江自流')
time.sleep(1)
def main():
a = '落霞与孤鹜齐飞,秋水共长天一色'
t = MyThread(a)
t.start()
t.join()
print('主线程、子线程全部结束')
if __name__ == '__main__':
main()
是否共享
全局变量
共享全局变量
局部变量
不共享局部变量
线程安全问题
线程之间可以共享全局变量
import threading
import os
import time
count = 100
# 该线程用来修改全局变量的值
def change():
global count
count += 100
print('线程%s修改后的值为%s' % (threading.current_thread().name, count))
# 该线程用来读取全局变量的值
def read():
print('线程%s读取的值为%s' % (threading.current_thread().name, count))
def test():
a = 100
name = threading.current_thread().name
if name == 'lala':
a += 100
time.sleep(2)
print('线程%s修改a的值为%s' % (name, a))
else:
time.sleep(5)
print('线程读取a的值为%s' % a)
def main():
'''
t1 = threading.Thread(target=change, name='修改thread')
t1.start()
time.sleep(2)
t2 = threading.Thread(target=read, name='读取thread')
t2.start()
'''
t1 = threading.Thread(target=test, name='lala')
t2 = threading.Thread(target=test)
t1.start()
t2.start()
t1.join()
t2.join()
if __name__ == '__main__':
main()
加锁
加锁
lock.acquire()
释放锁
lock.release()
import threading
import time
count = 100
# 创建一把锁
lock = threading.Lock()
def test(number):
start = time.time()
# 在这里面修改count的值
global count
for x in range(1, 10000000):
# 加锁
lock.acquire()
count += number
count -= number
# 释放锁
lock.release()
end = time.time()
# 上厕所,大号,如何解决,加锁
# 用之前,加锁,用完之后,释放锁
# 牺牲了效率了
print('循环计算的时间为%s' % (start - end))
def main():
t1 = threading.Thread(target=test, args=(3, ))
t2 = threading.Thread(target=test, args=(5, ))
t2.start()
t1.start()
t1.join()
t2.join()
print('主线程中打印的count的值为%s' % count)
if __name__ == '__main__':
main()
队列
队列:买火车票,电动三轮,特点:先进先出
用在哪?
线程之间使用队列进行交互,让线程解耦合,生产者-消费者模型
线程1-生产数据
交互渠道:队列
线程2-消费数据
while 1:
生产数据
消费数据
队列使用:
from queue import Queue
q = Queue(5)
q.put() 添加元素
q.put(False) 如果队列已满,添加元素立即抛出异常
q.put(True, 5) 如果队列已满,添加元素5s之后抛出异常
q.get() 获取元素
q.get(False) 如果队列为空,获取元素立即抛出异常
q.get(True, 5) 如果队列为空,获取元素5s之后抛出异常
q.empty() 队列是否为空
q.full() 队列是否已满
q.qsize() 队列长度
from queue import Queue
# 创建一个队列
# 如果写,代表队列的长度,如果不写,队列长度无限
q = Queue(5)
print(q.empty())
print(q.full())
print(q.qsize())
# 向队列中添加数据
q.put('吴彦祖')
q.put('岳云鹏')
q.put('王宝强')
q.put('黄渤')
q.put('刘德华')
print(q.empty())
print(q.full())
print(q.qsize())
# q.put('古天乐', True, 5)
# 从队列中获取数据
print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.get())
# print(q.get(True, 5))
多线程爬虫
分析:
爬虫里面如何分多线程,
循环:
拼接url,发送请求,获取响应
解析响应,保存到文件
涉及到:
采集线程,3个线程同时采集
解析线程,3个线程同时解析
页码队列:里面是要爬取的页码数据
数据队列:采集线程向队列中添加数据
解析线程从队列中获取数据
保存到同一个文件中,锁机制
import threading
from queue import Queue
import time
from lxml import etree
import requests
import json
class CrawlThread(threading.Thread):
def __init__(self, name, page_queue, data_queue):
super().__init__()
self.name = name
# 保存页码队列
self.page_queue = page_queue
self.data_queue = data_queue
# url
self.url = 'http://www.fanjian.net/duanzi-{}'
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
def run(self):
print('%s线程开始启动' % self.name)
# 这里面的思路是什么?
while 1:
if self.page_queue.empty():
break
# 1、从页码队列中获取页码
page = self.page_queue.get()
# 2、将url和页码进行拼接
url = self.url.format(page)
# 3、发送请求,获取响应
r = requests.get(url=url, headers=self.headers)
time.sleep(1)
# 4、将响应内容放入到数据队列中
self.data_queue.put(r.text)
print('%s线程结束' % self.name)
class ParseThread(threading.Thread):
def __init__(self, name, data_queue, lock, fp):
super().__init__()
self.name = name
# 保存数据队列
self.data_queue = data_queue
self.lock = lock
self.fp = fp
def run(self):
print('%s线程开始启动' % self.name)
# 解析线程解析步骤
while 1:
if self.data_queue.empty():
break
# 1、从数据队列中取出一个数据
content = self.data_queue.get()
# 2、解析这个数据
items = self.parse_content(content)
# 3、写入到文件中
string = json.dumps(items, ensure_ascii=False)
# 加锁
self.lock.acquire()
self.fp.write(string)
# 释放锁
self.lock.release()
print('%s线程结束' % self.name)
# 解析数据函数
def parse_content(content):
# 生成tree对象
tree = etree.HTML(content)
# 先找到所有的li标签
li_list = tree.xpath('//li[@class="cont-item"]')
items = []
for oli in li_list:
# 获取头像
face = oli.xpath('.//div[@class="cont-list-reward"]//img/@data-src')[0]
# 获取名字
name = oli.xpath('.//div[@class="cont-list-head"]/a/text()')[0]
# 获取内容
text = oli.xpath('.//div[@class="cont-list-main"]/p/text()')[0]
# 获取时间
shijian = oli.xpath('.//div[@class="cont-list-info fc-gray"]/text()')[-1]
item = {
'头像': face,
'名字': name,
'内容': text,
'时间': shijian,
}
# 将字典添加到列表中
items.append(item)
return items
def create_queue():
page_queue = Queue()
data_queue = Queue()
# 向页码队列中添加页码
for page in range(1, 11):
page_queue.put(page)
return page_queue, data_queue
def main():
# 做什么?
# 创建锁
lock = threading.Lock()
# 打开文件
fp = open('duanzi.txt', 'w', encoding='utf8')
# 创建两个队列
page_queue, data_queue = create_queue()
# 创建采集、解析线程
crawlname_list = ['采集线程1', '采集线程2', '采集线程3']
parsename_list = ['解析线程1', '解析线程2', '解析线程3']
# 列表,用来保存所有的采集线程和解析线程
t_crawl_list = []
t_parse_list = []
for crawlname in crawlname_list:
t_crawl = CrawlThread(crawlname, page_queue, data_queue)
t_crawl.start()
# 将对应的采集线程保存起来
t_crawl_list.append(t_crawl)
for parsename in parsename_list:
t_parse = ParseThread(parsename, data_queue, lock, fp)
# 将对应的解析线程保存起来
t_parse_list.append(t_parse)
t_parse.start()
# 让主线程等待子线程结束之后再结束
for t_crawl in t_crawl_list:
t_crawl.join()
for t_parse in t_parse_list:
t_parse.join()
fp.close()
print('主线程、子线程全部结束')
if __name__ == '__main__':
main()
# 留给大家了,为什么里面没有写数据呢?
自动识别验证码
(1)光学识别 OCR 其实就是一个软件
- 别对他期望太高,识别率80% 90%
*训练它
代码识别
pip install pytesseract
pip install pillow
转化为灰度图片
img = img.convert('L')
img.show()
二值化处理
threshold = 140
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)
out = img.point(table, '1')
out.show()
img = img.convert('RGB')
enhancer = ImageEnhance.Color(img)
enhancer = enhancer.enhance(0)
enhancer = ImageEnhance.Brightness(enhancer)
enhancer = enhancer.enhance(2)
enhancer = ImageEnhance.Contrast(enhancer)
enhancer = enhancer.enhance(8)
enhancer = ImageEnhance.Sharpness(enhancer)
img = enhancer.enhance(20)
实例代码如下
import pytesseract
from PIL import Image
from PIL import ImageEnhance
def shibie(imagepath):
img = Image.open(imagepath)
img = img.convert('RGB')
enhancer = ImageEnhance.Color(img)
enhancer = enhancer.enhance(0)
enhancer = ImageEnhance.Brightness(enhancer)
enhancer = enhancer.enhance(2)
enhancer = ImageEnhance.Contrast(enhancer)
enhancer = enhancer.enhance(8)
enhancer = ImageEnhance.Sharpness(enhancer)
img = enhancer.enhance(20)
# 转化为灰度图片
img = img.convert('L')
# img.show()
# 二值化处理
threshold = 140
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)
out = img.point(table, '1')
# out.show()
img = img.convert('RGB')
return pytesseract.image_to_string(img)
shibie('code.png')
打码平台,专业,识别率高,花钱
云打码平台使用
用户注册(充钱)、开发者注册(创建应用,使用应用识别验证码服务)
import http.client, mimetypes, urllib, json, time, requests
######################################################################
class YDMHttp:
apiurl = 'http://api.yundama.com/api.php'
username = ''
password = ''
appid = ''
appkey = ''
def __init__(self, username, password, appid, appkey):
self.username = username
self.password = password
self.appid = str(appid)
self.appkey = appkey
def request(self, fields, files=[]):
response = self.post_url(self.apiurl, fields, files)
response = json.loads(response)
return response
def balance(self):
data = {'method': 'balance', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey}
response = self.request(data)
if (response):
if (response['ret'] and response['ret'] < 0):
return response['ret']
else:
return response['balance']
else:
return -9001
def login(self):
data = {'method': 'login', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey}
response = self.request(data)
if (response):
if (response['ret'] and response['ret'] < 0):
return response['ret']
else:
return response['uid']
else:
return -9001
def upload(self, filename, codetype, timeout):
data = {'method': 'upload', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'codetype': str(codetype), 'timeout': str(timeout)}
file = {'file': filename}
response = self.request(data, file)
if (response):
if (response['ret'] and response['ret'] < 0):
return response['ret']
else:
return response['cid']
else:
return -9001
def result(self, cid):
data = {'method': 'result', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'cid': str(cid)}
response = self.request(data)
return response and response['text'] or ''
def decode(self, filename, codetype, timeout):
cid = self.upload(filename, codetype, timeout)
if (cid > 0):
for i in range(0, timeout):
result = self.result(cid)
if (result != ''):
return cid, result
else:
time.sleep(1)
return -3003, ''
else:
return cid, ''
def report(self, cid):
data = {'method': 'report', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'cid': str(cid), 'flag': '0'}
response = self.request(data)
if (response):
return response['ret']
else:
return -9001
def post_url(self, url, fields, files=[]):
for key in files:
files[key] = open(files[key], 'rb');
res = requests.post(url, files=files, data=fields)
return res.text
######################################################################
# 用户名
# 普通账号的用户名
username = ''
# 密码
# 普通账号的密码
password = ''
# 软件ID,开发者分成必要参数。登录开发者后台【我的软件】获得!
appid =
# 软件密钥,开发者分成必要参数。登录开发者后台【我的软件】获得!
appkey = ''
# 图片文件
filename = './png/e6ay.png'
# 验证码类型,# 例:1004表示4位字母数字,不同类型收费不同。请准确填写,否则影响识别率。在此查询所有类型 http://www.yundama.com/price.html
codetype = 1004
# 超时时间,秒
timeout = 60
# 检查
if (username == 'username'):
print('请设置好相关参数再测试')
else:
# 初始化
yundama = YDMHttp(username, password, appid, appkey)
# 登陆云打码
uid = yundama.login();
print('uid: %s' % uid)
# 查询余额
balance = yundama.balance();
print('balance: %s' % balance)
# 开始识别,图片路径,验证码类型ID,超时时间(秒),识别结果
cid, result = yundama.decode(filename, codetype, timeout);
print('cid: %s, result: %s' % (cid, result))
######################################################################