这里有个网站可以参考,下面都是实战小Demo
在Python3中包urllib2归入了urllib中,所以要导入urllib.request,并且要把urllib2替换成urllib.request
# python2
import urllib2
url = 'http://www.jianshu.com/trending/weekly?page={}'
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36'}
request = urllib2.Request(url=url, headers=headers)
html = urllib2.urlopen(request)
print html.read()
# python3
import urllib.request
url = 'http://www.jianshu.com/trending/weekly?page={}'
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36'}
request = urllib.request.Request(url=url, headers=headers)
# urllib.request.urlopen(url,data,timeout) :第一个参数url,第二个参数是访问url要传送的数据,第三个参数是设置超时的时间
html= urllib.request.urlopen(request)
print(html.read())
以上python3最简单的爬虫代码
1.Request
在我们第一个例子里,urlopen()的参数就是一个url地址;
但是如果需要执行更复杂的操作,比如增加HTTP报头,必须创建一个 Request 实例来作为urlopen()的参数;而需要访问的url地址则作为 Request 实例的参数。
data(默认空):是伴随 url 提交的数据(比如要post的数据),同时 HTTP 请求将从 "GET"方式 改为 "POST"方式。
headers(默认空):是一个字典,包含了需要发送的HTTP报头的键值对。
2.User-Agent
浏览器 就是互联网世界上公认被允许的身份,如果我们希望我们的爬虫程序更像一个真实用户,那我们第一步,就是需要伪装成一个被公认的浏览器。用不同的浏览器在发送请求的时候,会有不同的User-Agent头。 urllib默认的User-Agent头为:
Python-urllib/x.y
(x和y是Python主版本和次版本号,例如 Python-urllib/3.6)
在 HTTP Request 中加入特定的 Header,来构造一个完整的HTTP请求消息。
可以通过调用
Request.add_header()
添加/修改一个特定的header 也可以通过调用Request.get_header()
来查看已有的header。
3.url编码
from urllib import request, response, parse
import ssl
# 基本访问
# 忽略https校验
ssl._create_default_https_context = ssl._create_unverified_context
data = parse.urlencode({'wd':'宓珂璟'})
# URL参数编码
print(data)
# 汉字url编码
print(parse.quote('宓珂璟'))
# 解码
print(parse.unquote(data))
# wd=%E5%AE%93%E7%8F%82%E7%92%9F
# %E5%AE%93%E7%8F%82%E7%92%9F
# wd=宓珂璟
request = request.urlopen('http://tieba.baidu.com')
print(request.status) # 状态码
print(request.getheaders()) # 请求头
print(request.getheader('Content-Type')) # 单个值
# print(request.read().decode('utf-8'))
import urllib.request
import urllib.parse
import ssl
# 贴吧get爬虫
def loadurls(url,filename):
print('正在下载%s........'%filename)
header = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1 Safari/605.1.15",}
request = urllib.request.Request(url, headers=header)
res = urllib.request.urlopen(request).read().decode('utf-8')
print('下载完毕%s'%filename)
print('*'*50)
return res
def writedatas(res, filename):
with open(filename, 'w') as f:
f.write(res)
print(filename + '下载完毕')
def tiebaSpider(url, begin, end):
for index in range(int(begin), int(end) + 1):
page = (index - 1) * 50
filename = str(index) + 'html'
print('搜索到第' + str(index) + 'html')
fullurl = url + '&pn=' + str(page)
res = loadurls(fullurl, filename)
writedatas(res, filename)
print('谢谢使用')
if __name__ == '__main__':
ssl._create_default_https_context = ssl._create_unverified_context
kw = input('请输入搜索关键字')
begin = input('请输入起始页面')
end = input('请输入结束页面')
url = 'http://tieba.baidu.com/f?' + urllib.parse.urlencode({'kw':kw})
# print(url)
# print('%s,%s,%s'%(type(kw),type(begin),type(end)))
tiebaSpider(url, begin, end)
参数都可以通过Charles抓包得到
import urllib.request
import urllib.parse
import ssl
import hashlib
import time
import random
import json
# 有道post翻译
url = 'http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
headers = {
"Accept": "application/json, text/javascript, */*; q=0.01",
"X-Requested-With": "XMLHttpRequest",
"Accept-Language": "zh-cn",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1 Safari/605.1.15",
}
key = input('请输入你要翻译的单词:')
formdata = {
"i": key,
"from": "AUTO",
"to": "AUTO",
"smartresult": "dict",
"client": "fanyideskweb",
"doctype": "json",
"version": "2.1",
"keyfrom": "fanyi.web",
"action": "FY_BY_REALTIME",
"typoResult": "false",
}
# url参数都要进行转码 而且是二进制
data = bytes(urllib.parse.urlencode(formdata), encoding='utf-8')
request = urllib.request.Request(url, data=data, headers=headers)
response = urllib.request.urlopen(request)
line = json.load(response)
print(line)
print(line.get('translateResult', '')[0][0].get('tgt'))
# 请输入你要翻译的单词:my name is lucy i love USA
# {'type': 'EN2ZH_CN', 'errorCode': 0, 'elapsedTime': 11, 'translateResult': [[{'src': 'my name is lucy i love USA', 'tgt': '我的名字是露西我爱美国'}]]}
# 我的名字是露西我爱美国
Content-Length: xxx
: 是指发送的表单数据长度为xxx,也就是字符个数是xxx个。
X-Requested-With: XMLHttpRequest
:表示Ajax异步请求。
Content-Type: application/x-www-form-urlencoded
: 表示浏览器提交 Web 表单时使用,表单数据会按照 name1=value1&name2=value2 键值对形式进行编码。
import urllib.parse, urllib.request
import json
import ssl
# 豆瓣ajax爬虫
ssl._create_default_https_context = ssl._create_unverified_context
url = 'https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action='
header = {
"User-Agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1 Safari/605.1.15"
}
formdata = {
"start":"0",
"limit":"20"
}
# print(urllib.parse.quote(url))
# print(urllib.parse.urlencode(formdata))
data = bytes(urllib.parse.urlencode(formdata), encoding='utf-8')
request = urllib.request.Request(url, headers=header, data=data)
response = urllib.request.urlopen(request)
with open('movie.json', 'w') as j:
j.write(response.read().decode('utf-8'))
print(response.read().decode('utf-8'))
处理HTTPS请求 SSL证书验证,上面都有屏蔽掉
现在随处可见 https 开头的网站,urllib可以为 HTTPS 请求验证SSL证书,就像web浏览器一样,如果网站的SSL证书是经过CA认证的,则能够正常访问,如:https://www.baidu.com/
等...
如果SSL证书验证不通过,或者操作系统不信任服务器的安全证书,比如浏览器在访问12306网站如:https://www.12306.cn/mormhweb/的时候,会警告用户证书不受信任。(据说 12306 网站证书是自己做的,没有通过CA认证)
我们之前一直都在使用的urlopen,它是一个特殊的opener(也就是模块帮我们构建好的)。
但是基本的urlopen()方法不支持代理、cookie等其他的HTTP/HTTPS高级功能。所以要支持这些功能:
Handler处理器
来创建特定功能的处理器对象;urllib.request.build_opener()
方法使用这些处理器对象,创建自定义opener对象;open()
方法发送请求。如果程序里所有的请求都使用自定义的opener,可以使用urllib.request.install_opener()
将自定义的 opener 对象 定义为 全局opener,表示如果之后凡是调用urlopen,都将使用这个opener(根据自己的需求来选择)
import urllib.request
# 自定义handler
# 构建httphandler debuglevel=1自动打开debug模式
http_handler = urllib.request.HTTPHandler(debuglevel=1)
# 通过handerler构建opener
opener = urllib.request.build_opener(http_handler)
# 请求对象
request = urllib.request.Request('http://www.baidu.com')
# openner对象 open调用请求
response = opener.open(request)
print(response.read().decode('utf-8'))
免费代理模式
import urllib.request
# 免费共有代理
# proxy 代理服务器 抓包
# 1、User-Agent 反抓包
# 2、Proxy 代理 反抓包
#proxy_list = [
# {"http" : "124.88.67.81:80"},
# {"http" : "124.88.67.81:80"},
# {"http" : "124.88.67.81:80"},
# {"http" : "124.88.67.81:80"},
# {"http" : "124.88.67.81:80"}
#]
# 随机选择一个代理
#proxy = random.choice(proxy_list)
# 是否启用代理
proxyswitch = False
# 代理
proxyhandler = urllib.request.ProxyHandler({'http': '122.237.105.144:80'})
# 不代理
nonhandler = urllib.request.ProxyHandler({})
# 创建opner
if proxyswitch:
opner = urllib.request.build_opener(proxyhandler)
else:
opner = urllib.request.build_opener(nonhandler)
# 可以直接 使用opner 该方法是局部生效的
# opner.open(request)
# 如果要让全局opner使用自己创建的代理opner,就需要注入全局 之后就可以用urlopne老方法发送了
urllib.request.install_opener(opner)
url = 'http://www.baidu.com'
headers = {
}
request = urllib.request.Request(url, headers=headers)
response = urllib.request.urlopen(request)
print(response.read())
私有代理模式
import urllib.request
import os
# 使用系统变量记录账号密码
# ~/.bash_profile 修改
# 购买私密代理共有代理
name = os.environ.get('proxyname')
pwd = os.environ.get('proxypwd')
# 代理
proxyhandler = urllib.request.ProxyHandler({'http': name + ':' + pwd + '@122.237.105.144:80'})
# 创建opner
opner = urllib.request.build_opener(proxyhandler)
url = 'http://www.baidu.com'
headers = {
}
request = urllib.request.Request(url, headers=headers)
response = opner.open(request)
print(response.read())
这里我们用系统全局变量来记录账号密码 ~/.bash_profile里面导出
这是用来记录账号和密码进行校验用的
HTTPPasswordMgrWithDefaultRealm()
类将创建一个密码管理对象,用来保存 HTTP 请求相关的用户名和密码,主要应用两个场景:
- 验证代理授权的用户名和密码 (
ProxyBasicAuthHandler()
)- 验证Web客户端的的用户名和密码 (
HTTPBasicAuthHandler()
)HTTPPasswordMgrWithDefaultRealm()
:来保存私密代理的用户密码ProxyBasicAuthHandler()
:来处理代理的身份验证。
1.ProxyBasicAuthHandler 该处理器我们不用先,太麻烦,不如我们上面直接拼接到ProxyHandler
2.HTTPBasicAuthHandler处理器(Web客户端授权验证)
import urllib
# 用户名
user = "test"
# 密码
passwd = "123456"
# Web服务器 IP
webserver = "http://192.168.1.107"
# 1. 构建一个密码管理对象,用来保存需要处理的用户名和密码
passwdmgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
# 2. 添加账户信息,第一个参数realm是与远程服务器相关的域信息,一般没人管它都是写None,后面三个参数分别是 Web服务器、用户名、密码
passwdmgr.add_password(None, webserver, user, passwd)
# 3. 构建一个HTTP基础用户名/密码验证的HTTPBasicAuthHandler处理器对象,参数是创建的密码管理对象
httpauth_handler = urllib.request.HTTPBasicAuthHandler(passwdmgr)
# 4. 通过 build_opener()方法使用这些代理Handler对象,创建自定义opener对象,参数包括构建的 proxy_handler
opener = urllib.request.build_opener(httpauth_handler)
# 5. 可以选择通过install_opener()方法定义opener为全局opener
urllib.request.install_opener(opener)
# 6. 构建 Request对象
request = urllib.request.Request("http://192.168.1.107")
# 7. 定义opener为全局opener后,可直接使用urlopen()发送请求
response = urllib.request.urlopen(request)
# 8. 打印响应内容
print response.read()
Python3 NameError: name 'cookielib' is not defined
import http.cookiejar 用这个
Cookie 是指某些网站服务器为了辨别用户身份和进行Session跟踪,而储存在用户浏览器上的文本文件,Cookie可以保持登录信息到用户下次与服务器的会话。
HTTP是无状态的面向连接的协议, 为了保持连接状态, 引入了Cookie机制 Cookie是http消息头中的一种属性,包括:
Cookie名字(Name)
Cookie的值(Value)
Cookie的过期时间(Expires/Max-Age)
Cookie作用路径(Path)
Cookie所在域名(Domain),
使用Cookie进行安全连接(Secure)。
前两个参数是Cookie应用的必要条件,另外,还包括Cookie大小(Size,不同浏览器对Cookie个数及大小限制是有差异的)。
Cookie由变量名和值组成,根据 Netscape公司的规定,Cookie格式如下:
Set-Cookie: NAME=VALUE;Expires=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE
案例一:这里不贴代码了,无非就是我们手动登录某个网站,前提是最简单的网站,登录的时候没有动态token机制。我们登录之后,抓包拿到cookies,然后我们访问登录后的页面,如果代码直接访问,而且把cookies带在请求头上,那么依然是可以访问的,如果没有cookies,就会出现跳到登录页面的情况。这种手动方式获取cookies,下面看下cookies对象自动登录获取存储,下一次再次请求的时候自动使用
import urllib
import http.cookiejar
# 1. 构建一个CookieJar对象实例来保存cookie
cookiejar = http.cookiejar.CookieJar()
# 2. 使用HTTPCookieProcessor()来创建cookie处理器对象,参数为CookieJar()对象
cookies_handle = urllib.request.HTTPCookieProcessor(cookiejar)
# 3. 通过 build_opener() 来构建opener
opener = urllib.request.build_opener(cookies_handle)
# 4. addheaders 接受一个列表,里面每个元素都是一个headers信息的元祖, opener将附带headers信息
opener.addheaders = [("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36")]
# 5. 需要登录的账户和密码
data = {"email":"xxxx", "password":"xxxx"}
# 6. 通过urlencode()转码
postdata = urllib.parse.urlencode(data)
# 7. 构建Request请求对象,包含需要发送的用户名和密码
request = urllib.request.Request("http://www.renren.com/PLogin.do", data = postdata)
# 8. 通过opener发送这个请求,并获取登录后的Cookie值,
opener.open(request)
# 9. opener包含用户登录后的Cookie值,可以直接访问那些登录后才可以访问的页面
response = opener.open("http://www.renren.com/410043129/profile")
# 10. 打印响应内容
print response.read()
以上就是Python3下面发送请求的一些基本操作
Requests 唯一的一个非转基因的 Python HTTP 库,人类可以安全享用。
警告:非专业使用其他 HTTP 库会导致危险的副作用,包括:安全缺陷症、冗余代码症、重新发明轮子症、啃文档症、抑郁、头疼、甚至死亡。看着文档操作即可
http://docs.python-requests.org/zh_CN/latest/index.html
上面介绍的无非就是爬下来,如何取出我们需要的数据,就需要正则匹配
re 模块的一般使用步骤如下:
使用 compile()
函数将正则表达式的字符串形式编译为一个 Pattern
对象
通过 Pattern
对象提供的一系列方法对文本进行匹配查找,获得匹配结果,一个 Match 对象。
Match
对象提供的属性和方法获得信息,根据需要进行其他的操作在上面,我们已将一个正则表达式编译成 Pattern 对象,接下来,我们就可以利用 pattern 的一系列方法对文本进行匹配查找了。
Pattern 对象的一些常用方法主要有:
- match 方法:从起始位置开始查找,一次匹配
- search 方法:从任何位置开始查找,一次匹配
- findall 方法:全部匹配,返回列表
- finditer 方法:全部匹配,返回迭代器
- split 方法:分割字符串,返回列表
- sub 方法:替换
match 方法用于查找字符串的头部(也可以指定起始位置),它是一次匹配,只要找到了一个匹配的结果就返回,而不是查找所有匹配的结果。它的一般使用形式如下:
match(string[, pos[, endpos]])
其中,string 是待匹配的字符串,pos 和 endpos 是可选参数,指定字符串的起始和终点位置,默认值分别是 0 和 len (字符串长度)。因此,当你不指定 pos 和 endpos 时,match 方法默认匹配字符串的头部。
当匹配成功时,返回一个 Match 对象,如果没有匹配上,则返回 None。
In [45]: import re
In [46]: pattern = re.compile(r'([a-z]+) ([a-z]+)',re.I)
In [47]: m = pattern.match('Hello World Mikejing Python3')
In [48]: print(m)
<_sre.SRE_Match object; span=(0, 11), match='Hello World'>
In [49]: m.groups()
Out[49]: ('Hello', 'World')
In [50]: m.group()
Out[50]: 'Hello World'
In [51]: m.group(0)
Out[51]: 'Hello World'
In [52]: m.group(1)
Out[52]: 'Hello'
In [53]: m.group(2)
Out[53]: 'World'
In [54]: m.group(3)
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
in ()
----> 1 m.group(3)
IndexError: no such group
In [55]: m.span()
Out[55]: (0, 11)
In [56]: m.span(0)
Out[56]: (0, 11)
In [57]: m.span(1)
Out[57]: (0, 5)
In [58]: m.span(2)
Out[58]: (6, 11)
search 方法
search 方法用于查找字符串的任何位置,它也是一次匹配,只要找到了一个匹配的结果就返回,而不是查找所有匹配的结果,它的一般使用形式如下:
search(string[, pos[, endpos]])
其中,string 是待匹配的字符串,pos 和 endpos 是可选参数,指定字符串的起始和终点位置,默认值分别是 0 和 len (字符串长度)。
当匹配成功时,返回一个 Match 对象,如果没有匹配上,则返回 None。
import re
# 将正则表达式编译成 Pattern 对象
pattern = re.compile(r'\d+')
# 使用 search() 查找匹配的子串,不存在匹配的子串时将返回 None
# 这里使用 match() 无法成功匹配
m = pattern.search('hello 123456 789')
if m:
# 使用 Match 获得分组信息
print ('matching string:',m.group())
# 起始位置和结束位置
print ('position:',m.span())
findall 和 finditer
上面的 match 和 search 方法都是一次匹配,只要找到了一个匹配的结果就返回。然而,在大多数时候,我们需要搜索整个字符串,获得所有匹配的结果。
findall 方法的使用形式如下:
findall(string[, pos[, endpos]]) 返回数组
finditer(string[, pos[, endpos]]) 返回可迭代对象
其中,string 是待匹配的字符串,pos 和 endpos 是可选参数,指定字符串的起始和终点位置,默认值分别是 0 和 len (字符串长度)。
findall 以列表形式返回全部能匹配的子串,如果没有匹配,则返回一个空列表。
import re
#re模块提供一个方法叫compile模块,提供我们输入一个匹配的规则
#然后返回一个pattern实例,我们根据这个规则去匹配字符串
pattern = re.compile(r'\d+\.\d*')
#通过partten.findall()方法就能够全部匹配到我们得到的字符串
result = pattern.findall("123.141593, 'bigcat', 232312, 3.15")
#findall 以 列表形式 返回全部能匹配的子串给result
for item in result:
print (item)
运行结果:
123.141593
3.15
import re
pattern = re.compile(r'\d+')
result_iter1 = pattern.finditer('hello 123456 789')
result_iter2 = pattern.finditer('one1two2three3four4', 0, 10)
print (type(result_iter1))
print (type(result_iter2))
print ('result1...')
for m1 in result_iter1: # m1 是 Match 对象
print ('matching string: {}, position: {}'.format(m1.group(), m1.span()))
print ('result2...')
for m2 in result_iter2:
print ('matching string: {}, position: {}'.format(m2.group(), m2.span()))
执行结果:
result1...
matching string: 123456, position: (6, 12)
matching string: 789, position: (13, 16)
result2...
matching string: 1, position: (3, 4)
matching string: 2, position: (7, 8)
split 方法
split 方法按照能够匹配的子串将字符串分割后返回列表,它的使用形式如下:
split(string[, maxsplit])
其中,maxsplit 用于指定最大分割次数,不指定将全部分割。
In [90]: pa = re.compile(r'[\s\d\\\;]+')
In [91]: m = pa.split('a bb\aa;mm; a')
In [92]: m
Out[92]: ['a', 'bb\x07a', 'mm', 'a']
In [93]: m = pa.split(r'a bb\aa;mm; a')
In [94]: m
Out[94]: ['a', 'bb', 'aa', 'mm', 'a']
In [95]: m = pa.split(r'a bb\aa;mdm; a')
In [96]: m
Out[96]: ['a', 'bb', 'aa', 'mdm', 'a']
In [97]: pa = re.compile('[\s\d\\\;]+')
In [98]: m = pa.split(r'a bb\aa;mdm; a')
In [99]: m
Out[99]: ['a', 'bb', 'aa', 'mdm', 'a']
这里匹配字符串如果不加r不管转义,那么\a就不是两个字符了,而是当成一个asic码,需要用r来标识不转移,然后再切割
sub 方法用于替换。它的使用形式如下:
sub(repl, string[, count])
其中,repl 可以是字符串也可以是一个函数:
如果 repl 是字符串,则会使用 repl 去替换字符串每一个匹配的子串,并返回替换后的字符串,另外,repl 还可以使用 id 的形式来引用分组,但不能使用编号 0;
如果 repl 是函数,这个方法应当只接受一个参数(Match 对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。
import re
p = re.compile(r'(\w+) (\w+)') # \w = [A-Za-z0-9]
s = 'hello 123, hello 456'
print p.sub(r'hello world', s) # 使用 'hello world' 替换 'hello 123' 和 'hello 456'
print p.sub(r'\2 \1', s) # 引用分组
def func(m):
return 'hi' + ' ' + m.group(2)
print (p.sub(func, s))
print (p.sub(func, s, 1)) # 最多替换一次
执行结果:
hello world, hello world
123 hello, 456 hello
hi 123, hi 456
hi 123, hello 456
https://juejin.im/entry/5805dcc85bbb50005b864405
这里的意思是,贪婪模式直接从段尾开始匹配,非贪婪就是正常往下匹配到第一组满足的
示例一 : 源字符串:abbbc
ab*
,匹配结果: abbb。
*
决定了尽可能多匹配 b,所以a后面所有的 b 都出现了。
ab*?
,匹配结果: a。
即使前面有
*
,但是?
决定了尽可能少匹配 b,所以没有 b。
下面这个例子很好诠释了区别
In [1]: import re
In [2]: patnner = re.compile(r'ab*')
In [3]: m = patnner.match('abbbbbbbbbb')
In [4]: m
Out[4]: <_sre.SRE_Match object; span=(0, 11), match='abbbbbbbbbb'>
In [5]: m.group()
Out[5]: 'abbbbbbbbbb'
In [6]: patnner = re.compile(r'ab*?')
In [7]: m = patnner.match('abbbbbbbbbb')
In [8]: m
Out[8]: <_sre.SRE_Match object; span=(0, 1), match='a'>
In [9]: m.group()
Out[9]: 'a'
import re
import urllib.request
import ssl
class Spider:
# 初始化
def __init__(self):
# 控制页数
self.page = 1
# 控制是否继续爬取
self.switch = True
# 请求网页数据
def loadPage(self):
print('正在爬取数据。。。。。。')
url = 'https://www.neihan8.com/article/list_5_' + str(self.page) + '.html'
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1 Safari/605.1.15"
}
# request对象
request = urllib.request.Request(url, headers=headers)
# 发送
response = urllib.request.urlopen(request)
# 不转码打印出来的是二进制的 需要根据网页编码方式转码,一般国际就是utf-8,这里是gbk的
html = response.read().decode('gb2312"')
print('爬取数据完毕。。。。。。')
# 正则匹配,这里两个div之间获取数据如果用 .*默认是贪婪模式,会从文末匹配出最后一个div,加了?就非贪婪,从头开始匹配满足的第一个
patterner = re.compile('(.*?)', re.S)
# 全文检索 返回list
textlist = patterner.findall(html)
self.dealPage(textlist)
# 处理数据
def dealPage(self, htmllist):
for con in htmllist:
print('正在写入数据数据。。。。。。。')
con = str(con).replace('','').replace('
','').replace('
','').replace('
','').replace(' ','').replace('\t','')
self.writePage(con)
print('写入数据数据完毕。。。。。。。')
# 写入数据到文本 a+是追加的方式 w是清楚覆盖方式
def writePage(self, content):
with open('neihan.txt', 'a+') as f:
# f.write('\n')
f.write(content)
# f.write('\n')
f.write('*'*200)
# 开始方法
def startSpider(self):
print('欢迎进入爬虫小Demo,开始爬虫')
while self.switch:
self.loadPage()
pp = input('如果您要继续爬数据,直接按下回车,如果你不想继续,请输入quit')
if pp == 'quit':
self.switch = False
self.page += 1
print('谢谢使用')
if __name__ == '__main__':
ssl._create_default_https_context = ssl._create_unverified_context
spider = Spider()
spider.startSpider()
1.还是用Python3自带的urllib来请求数据
2.'
3.a+打开文本追加 结果保存在txt文件中即可
import urllib.request, urllib.parse
from lxml import etree
import ssl
import requests
import os
class Spider:
def __init__(self):
self.startpage = 0
self.endpage = 0
self.keywords = ''
self.url = 'https://tieba.baidu.com'
self.currentpage = 0
def loadPage(self):
data = {
'kw' : self.keywords,
'pn' : self.currentpage
}
# 参数编码
para = urllib.parse.urlencode(data)
url = self.url + '/f?' + para
print(url)
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1 Safari/605.1.15"
}
# 这里如果用urllin不知道什么原因,无法匹配
# 应该是user-agent导致的,如果要用就用ie的
# request = urllib.request.Request(url, headers=headers)
# response = urllib.request.urlopen(request)
response = requests.get(url, headers)
content = etree.HTML(response.text)
detainList = content.xpath('//div[@class="t_con cleafix"]/div/div/div/a/@href')
print(detainList)
for detaillink in detainList:
self.loadImage(self.url + detaillink)
def loadImage(self, detailurl):
print(detailurl)
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1 Safari/605.1.15"
}
# 请求帖子详情页面
response = requests.get(detailurl, headers)
content = etree.HTML(response.text)
# 爬出详情页面的图片资源
imageurls = content.xpath("//img[@class='BDE_Image']/@src")
for imgurl in imageurls:
print(imgurl)
self.writeImage(imgurl)
def writeImage(self, imageurl):
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1 Safari/605.1.15"
}
response = requests.get(imageurl, headers)
filename = imageurl[-10:]
imgfiles = os.getcwd() + '/meinv'
if os.path.exists(imgfiles):
print('文件夹存在')
else:
print('不存在,创建')
os.mkdir(imgfiles)
with open('meinv/' + filename,'wb') as img:
img.write(response.content)
def startSpider(self):
self.keywords = input('请输入需要爬取的贴吧名称:')
self.startpage = int(input('请输入起始页:'))
self.endpage = int(input('请输入结束页:'))
for index in range(self.startpage, self.endpage+1):
self.currentpage = (index - 1) * 50
self.loadPage()
if __name__ == '__main__':
ssl._create_default_https_context = ssl._create_unverified_context
spider = Spider()
spider.startSpider()
1.需要pip3 install lxml第三方包解析xpath
2.根据网页xpath搜集分析的数据用于匹配xml,首先根据关键字匹配贴吧所有帖子,继续打开贴吧帖子分析详情然后爬出每个详情的图片
3.这里还是用requests算了,urllib我是不能和lxml一起解析,有对应环境的直接copy走就能根据关键字下载图片了
先来看加xsrf这个东西的作用,我们之前写DJango的时候,会默认让form表单带上xsrftoken值。这里不过多介绍,网上有很多这样的介绍,例如知乎上的,博客上的,这里介绍了csrf的攻击原理,无非就是攻击网站钓鱼原来的浏览器,通过间接的方式访问原来的网站,获取到原网站的cookie再伪造form表单即可。之前开发iOS的时候,我们登录都是用token来获取的,每个用户登录都会生成一个token来标识登录态,每次带这个token后台会校验是否需要重新登录,这个token就好比后台传给我们保存在本地的,每次请求都会带给后台。这里也是一样,浏览器获取到后台给的token,我们每次提交form表单的时候带上这个token值,让后台校验,确保没有被攻击。攻击网站只能拿到对应的cookie,但是无法拿到表单的token,后台也就无法校验通过,起到了预防的作用。还有一些验证码之类的防范。
最后,对防御 CSRF 提出两种解决方案:
- 在每个表单中包含一个 CSRF Token.
- 不将用于认证的 Token 或 Seesion ID 储存在 Cookie 中,而是将其储存在 localStorage 中,在每次发起请求时手动将 Token 添加到请求中。
但是这里有个问题,攻击网站如果写个脚本把源网站的token也扒下来了,然后继续伪造带给后台,这个怎么破???有大神知道的话求指导。
#import urllib
import requests
from lxml import etree
import json
page = 1
url = 'http://www.qiushibaike.com/8hr/page/' + str(page)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36',
'Accept-Language': 'zh-CN,zh;q=0.8'}
try:
response = requests.get(url, headers=headers)
resHtml = response.text
html = etree.HTML(resHtml)
result = html.xpath('//div[contains(@id,"qiushi_tag")]')
print(len(result))
for site in result:
item = {}
imgUrl = site.xpath('./div[@class="thumb"]/a/img/@src')
imgUrl = imgUrl[0] if len(imgUrl)>0 else ''
username = site.xpath('./div/a/@title')[0]
content = site.xpath('.//div[@class="content"]/span')[0].text.strip()
item = {
'username':username,
'img':imgUrl,
'content':content
}
print(item)
with open('qiushi.json', 'a') as f:
f.write(json.dumps(item, ensure_ascii=False) + '\n')
except Exception as err:
print(err)
1.xpath查询出来都是数组,模糊查询//div[contains(@id,"qiushi_tag") 例如这样查询所有匹配的模块
2.如果获取的不是属性里面的内容,就需要.text获取
3.默认utf-8编码,dumps就不要指定编码方式了,默认的不需要改成False