博客
关于我
Python3.6爬虫记录
阅读量:360 次
发布时间:2019-03-05

本文共 23027 字,大约阅读时间需要 76 分钟。

Python2和Python3的区别

这里有个网站,下面都是实战小Demo

在Python3中包urllib2归入了urllib中,所以要导入urllib.request,并且要把urllib2替换成urllib.request

# python2import urllib2url = '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()
# python3import urllib.requesturl = '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 实例的参数。

  1. data(默认空):是伴随 url 提交的数据(比如要post的数据),同时 HTTP 请求将从 "GET"方式 改为 "POST"方式。

  2. 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, parseimport ssl# 基本访问# 忽略https校验ssl._create_default_https_context = ssl._create_unverified_contextdata = 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'))

 

Get

import urllib.requestimport urllib.parseimport 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 resdef 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)

POST

参数都可以通过Charles抓包得到

import urllib.requestimport urllib.parseimport sslimport hashlibimport timeimport randomimport 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 键值对形式进行编码。

Ajax

import urllib.parse, urllib.requestimport jsonimport ssl# 豆瓣ajax爬虫ssl._create_default_https_context = ssl._create_unverified_contexturl = '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网站如:的时候,会警告用户证书不受信任。(据说 12306 网站证书是自己做的,没有通过CA认证)

Handler处理器 和 自定义Opener

  • 我们之前一直都在使用的urlopen,它是一个特殊的opener(也就是模块帮我们构建好的)。

  • 但是基本的urlopen()方法不支持代理、cookie等其他的HTTP/HTTPS高级功能。所以要支持这些功能:

    1. 使用相关的 Handler处理器 来创建特定功能的处理器对象;
    2. 然后通过 urllib.request.build_opener()方法使用这些处理器对象,创建自定义opener对象;
    3. 使用自定义的opener对象,调用open()方法发送请求。
  • 如果程序里所有的请求都使用自定义的opener,可以使用urllib.request.install_opener() 将自定义的 opener 对象 定义为 全局opener,表示如果之后凡是调用urlopen,都将使用这个opener(根据自己的需求来选择)

简单的自定义 HTTPHandler

import urllib.request# 自定义handler# 构建httphandler  debuglevel=1自动打开debug模式http_handler =  urllib.request.HTTPHandler(debuglevel=1)# 通过handerler构建openeropener = 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'))

ProxyHandler处理器(代理设置)

免费代理模式

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({})# 创建opnerif 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.requestimport 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'})# 创建opneropner = 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()

这是用来记录账号和密码进行校验用的

HTTPPasswordMgrWithDefaultRealm()类将创建一个密码管理对象,用来保存 HTTP 请求相关的用户名和密码,主要应用两个场景:

  1. 验证代理授权的用户名和密码 (ProxyBasicAuthHandler())
  2. 验证Web客户端的的用户名和密码 (HTTPBasicAuthHandler())
  3. HTTPPasswordMgrWithDefaultRealm():来保存私密代理的用户密码
  4. ProxyBasicAuthHandler():来处理代理的身份验证。

1.ProxyBasicAuthHandler 该处理器我们不用先,太麻烦,不如我们上面直接拼接到ProxyHandler

2.HTTPBasicAuthHandler处理器(Web客户端授权验证)

import urllib# 用户名user = "test"# 密码passwd = "123456"# Web服务器 IPwebserver = "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_handleropener = urllib.request.build_opener(httpauth_handler)# 5. 可以选择通过install_opener()方法定义opener为全局openerurllib.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()

 

Cookies原理和使用

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 urllibimport http.cookiejar# 1. 构建一个CookieJar对象实例来保存cookiecookiejar = http.cookiejar.CookieJar()# 2. 使用HTTPCookieProcessor()来创建cookie处理器对象,参数为CookieJar()对象cookies_handle = urllib.request.HTTPCookieProcessor(cookiejar)# 3. 通过 build_opener() 来构建openeropener = 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下面发送请求的一些基本操作

Request(以上都可以用Request实现)

Requests 唯一的一个非转基因的 Python HTTP 库,人类可以安全享用。

警告:非专业使用其他 HTTP 库会导致危险的副作用,包括:安全缺陷症、冗余代码症、重新发明轮子症、啃文档症、抑郁、头疼、甚至死亡。看着文档操作即可

 

正则

上面介绍的无非就是爬下来,如何取出我们需要的数据,就需要正则匹配

re 模块的一般使用步骤如下:

  1. 使用 compile() 函数将正则表达式的字符串形式编译为一个 Pattern 对象

  2. 通过 Pattern 对象提供的一系列方法对文本进行匹配查找,获得匹配结果,一个 Match 对象。

  3. 最后使用 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 reIn [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)<ipython-input-54-974525c8e304> in <module>()----> 1 m.group(3)IndexError: no such groupIn [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 以 列表形式 返回全部能匹配的子串给resultfor item in result:    print (item)运行结果:123.1415933.15
import repattern = 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()))执行结果:<type 'callable-iterator'><type 'callable-iterator'>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]: mOut[92]: ['a', 'bb\x07a', 'mm', 'a']In [93]: m = pa.split(r'a bb\aa;mm;  a')In [94]: mOut[94]: ['a', 'bb', 'aa', 'mm', 'a']In [95]: m = pa.split(r'a bb\aa;mdm;  a')In [96]: mOut[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]: mOut[99]: ['a', 'bb', 'aa', 'mdm', 'a']

这里匹配字符串如果不加r不管转义,那么\a就不是两个字符了,而是当成一个asic码,需要用r来标识不转移,然后再切割

 

sub 方法用于替换。它的使用形式如下:

sub(repl, string[, count])

其中,repl 可以是字符串也可以是一个函数:

  • 如果 repl 是字符串,则会使用 repl 去替换字符串每一个匹配的子串,并返回替换后的字符串,另外,repl 还可以使用 id 的形式来引用分组,但不能使用编号 0;

  • 如果 repl 是函数,这个方法应当只接受一个参数(Match 对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。

  • count 用于指定最多替换次数,不指定时全部替换。
import rep = 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 world123 hello, 456 hellohi 123, hi 456hi 123, hello 456

贪婪模式和非贪婪模式

这里的意思是,贪婪模式直接从段尾开始匹配,非贪婪就是正常往下匹配到第一组满足的

  1. 贪婪模式:在整个表达式匹配成功的前提下,尽可能多的匹配 ( * );
  2. 非贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配 ( ? );
  3. Python里数量词默认是贪婪的。

示例一 : 源字符串:abbbc

  • 使用贪婪的数量词的正则表达式 ab* ,匹配结果: abbb。

    * 决定了尽可能多匹配 b,所以a后面所有的 b 都出现了。

  • 使用非贪婪的数量词的正则表达式ab*?,匹配结果: a。

    即使前面有 *,但是 ? 决定了尽可能少匹配 b,所以没有 b。

下面这个例子很好诠释了区别

In [1]: import reIn [2]: patnner = re.compile(r'ab*')In [3]: m = patnner.match('abbbbbbbbbb')In [4]: mOut[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]: mOut[8]: <_sre.SRE_Match object; span=(0, 1), match='a'>In [9]: m.group()Out[9]: 'a'

 

Demo1(内涵段子爬取案例 正则)

import reimport urllib.requestimport sslclass 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('<div\sclass="f18 mb20">(.*?)</div>', re.S)        # 全文检索 返回list        textlist = patterner.findall(html)                self.dealPage(textlist)            # 处理数据    def dealPage(self, htmllist):        for con in htmllist:            print('正在写入数据数据。。。。。。。')            con = str(con).replace('<p>','').replace('</p>','').replace('<br>','').replace('<br />','').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.'<div\sclass="f18 mb20">(.*?)</div>', 非贪婪模式匹配每个段子

3.a+打开文本追加 结果保存在txt文件中即可

 

Demo2(百度贴吧爬取所有图片 lxml xpath)

import urllib.request, urllib.parsefrom lxml import etreeimport sslimport requestsimport osclass 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走就能根据关键字下载图片了

 

_csrf的作用

先来看加xsrf这个东西的作用,我们之前写DJango的时候,会默认让form表单带上xsrftoken值。这里不过多介绍,网上有很多这样的介绍,例如,,这里介绍了csrf的攻击原理,无非就是攻击网站钓鱼原来的浏览器,通过间接的方式访问原来的网站,获取到原网站的cookie再伪造form表单即可。之前开发iOS的时候,我们登录都是用token来获取的,每个用户登录都会生成一个token来标识登录态,每次带这个token后台会校验是否需要重新登录,这个token就好比后台传给我们保存在本地的,每次请求都会带给后台。这里也是一样,浏览器获取到后台给的token,我们每次提交form表单的时候带上这个token值,让后台校验,确保没有被攻击。攻击网站只能拿到对应的cookie,但是无法拿到表单的token,后台也就无法校验通过,起到了预防的作用。还有一些验证码之类的防范。

     最后,对防御 CSRF 提出两种解决方案:

  1. 在每个表单中包含一个 CSRF Token.
  2. 不将用于认证的 Token 或 Seesion ID 储存在 Cookie 中,而是将其储存在 localStorage 中,在每次发起请求时手动将 Token 添加到请求中。

但是这里有个问题,攻击网站如果写个脚本把源网站的token也扒下来了,然后继续伪造带给后台,这个怎么破???有大神知道的话求指导。

 

Demo3(糗百xpath模糊查询)

#import urllibimport requestsfrom lxml import etreeimport jsonpage = 1url = '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

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的文章
监控264后缀文件播放
查看>>
php--class的工厂模式的示例
查看>>
jQuery练习t81
查看>>
使用async、await改善异步代码
查看>>
Idiot 的间谍网络
查看>>
初探SSRF漏洞
查看>>
pythonBug入门——从零开始学python
查看>>
js-禁止右键菜单代码、禁止复制粘贴代码
查看>>
SpringBoot中使用Mybatis访问MySQL数据库(使用xml方式)
查看>>
数组--Go语言学习笔记
查看>>
Redis (三)——Linux 上安装 Redis
查看>>
java 重写(override)和重载(overload)区别
查看>>
java 多态类型转换
查看>>
常用正则表达式
查看>>
XML:采用XHTML和CSS设计可重用可换肤的WEB站点
查看>>
Tomcat6中web项目部署路径webapps和wtpwebapps的区别
查看>>
Java判断字符串是否为金额
查看>>
软件架构-zookeeper快速入门
查看>>
angr学习笔记(7)(malloc地址单元符号化)
查看>>
「CF149D」括号涂色 区间DP好题
查看>>