爬虫

上网原理

上网原理.png

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的区别:

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软件
专业的抓包软件

配置:

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里面的非法字符进行编码,字母、数字、下划线、&、://

https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=78000241_9_hao_pg&wd=%E5%B8%85%E5%93%A5&oq=%25E5%25A7%259A%25E5%25B9%25BF%25E9%2591%25AB&rsv_pq=e7c2fda20000674b&rsv_t=9e64HlBZF4gMz%2Bxagmv2AtmJhzUYbJULwJToXPa78kQ%2BBslZuQB%2BshDyF%2BWpOTya0wv%2BZEwsI98&rqlang=cn&rsv_enter=1&inputT=44615&rsv_sug3=45&rsv_sug1=33&rsv_sug7=101&bs=%E5%A7%9A%E5%B9%BF%E9%91%AB

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请求

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.com

  • HTTPError: 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&currentPageNum=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&currentPageNum={}&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))

######################################################################

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容