urlencode相关知识
-
再说说php的urlencode和urldecode说多了都是泪,curl调用远程api接口, client端进行urlencode, 在server端urldecode后确实错的, 翻了一下php的手册,原来php对超全局变量$_GET $_REQUEST已经做了解码操作了,无需再次解码.ps:$_COOKIE需要urldecode!!!
-
php中urldecode()和stripslashes() 的作用urlencode()函数原理就是首先把中文字符转换为十六进制,然后在每个字符前面加一个标识符%。urldecode()函数与urlencode()函数原理相反,用于解码已编码的 URL 字符串,其原理就是把十六进制字符串转换为中文字符stripslashes() 函数实例删除反斜杠:<?phpecho stripslashes("Who\'s Bill Gates?");?>运行实例定义和用法stripslashes() 函数删除由 addslashes() 函数添加的反斜杠。提示:该函数可用于清理从数据库中或者从 HTML 表单中取回的数据。语法stripslashes(string)参数描述string必需。规定要检查的字符串。技术细节返回值:返回已剥离反斜杠的字符串。PHP 版本:4+
-
PHP输出json数据时,中文不进行unicode编码今天在写和API接口时,将PHP数组转成json,但是有个规则是不能用unicode编码试了几种方法:若PHP是5.4以上的 可以直接使用JSON_UNESCAPED_UNICODE参数 json_encode('中文测试',JSON_UNESCAPED_UNICODE); 这种方法字符串可行,但是数组不可行(有待验证) 2.将数组中的字符串urlencode,然后将数组json_encode转成json,最后进行urldecode注:urlencode()和urldecode()将中文字符转换为十六进制并按某种规则进行字符串组合,实现字符的编码与解编码,保证URL数据传递过程中字符的完整性和兼容性以上方法确实对中文避免了unicode编码,但是我最后在接口测试时,依旧没通过,反而是正常的json_encode后就可以,避免误人子弟,后续我会继续考证·····
-
Python爬虫简单笔记之BeautifulSoup4的简单用法记录一下Python爬虫常用库BeautifulSoup4的简单用法。其中demo均以自己的博客网站为对象。1 urllib和urllib2Python中包含了两个网络模块,分别是urllib与urllib2,urllib2是urllib的升级版,拥有更强大的功能。urllib,让我们可以像读文件一样,读取http与ftp。而urllib2,则在urllib的基础上,提供了更多的接口,如cookie、代理、认证等更强大的功能。这里借鉴下文章一和文章二的说法:urllib仅可以接受URL,不能创建,设置headers的request类实例;但是urllib提供urlencode()方法用来GET查询字符串的产生,而urllib2则没有(这是urllib和urllib2经常一起使用的主要原因)编码工作使用urllib的urlencode()函数,帮我们讲key:value这样的键值对转换成‘key=value’这样的字符串,解码工作可以使用urllib的unquote该两个库都是Python自带库,且由于简单爬
urlencode相关课程
urlencode相关教程
- 1.24 urlencode 对 URL 中的特殊字符进行转义。示例如下:{{ value|urlencode }}假设 value 值 为 https://www.example.org/foo?a=b&c=d", 上述过滤结果为: "https%3A//www.example.org/foo%3Fa%3Db%26c%3Dd"。
- 3. XSS攻击 XSS 攻击又称 CSS,全称Cross Site Script (跨站脚本攻击),其原理是攻击者向有 XSS 漏洞的网站中输入恶意的 HTML 代码,当用户浏览该网站时,这段 HTML 代码会自动执行,从而达到攻击的目的。XSS 攻击可以分成两种类型:非持久型 XSS 攻击:顾名思义,非持久型 XSS 攻击是一次性的,仅对当次的页面访问产生影响。非持久型 XSS 攻击要求用户访问一个被攻击者篡改后的链接,用户访问该链接时,被植入的攻击脚本被用户游览器执行,从而达到攻击目的;持久型 XSS 攻击:持久型 XSS,会把攻击者的恶意代码数据存储在服务器端,攻击行为将伴随着恶意代码的存在而一直存在。XSS 的攻击防范主要是对提交的各种字符串数据进行校验,避免出现可执行的前端代码。主要的防范措施有:对输入内容的特定字符进行编码,例如表示 html 标记的 < > 等符号;对重要的 cookie 设置 httpOnly,防止客户端通过 document.cookie 读取 cookie,此 HTTP 头在服务端设置;将不可信的值输出 URL 参数之前,进行 URLEncode 操作,而对于从 URL 参数中获取值一定要进行格式校验;不要使用 eval 来解析并运行不确定的数据或代码,对于 JSON 解析可使用 JSON.parse() 方法;后端接口也要对关键的字符进行过滤,防止将恶意的脚本代码保存到数据库中。
- 2. 创建一个自定义机器人 开启自定义机器人步骤,首先找到想要加入群机器人的钉钉群,在【群设置】-【智能群助手】,进入后如下图所示。选择【添加机器人】,进入到添加界面,如下图所示。在这里展示本群已加入的机器人,如需添加新的机器人,点击【添加机器人】进入到添加界面,如下图所示。在添加界面,选择自定义机器人,如下图所示。配置项解释:机器人名字:即添加到钉钉群中的机器人所显示的名称,本小节中命名为” 小 Q“添加到群组:即该机器人所加入的钉钉群,这里为只读显示,不可更改安全设置:自定义关键词:即当发送消息中包含至少一个指定关键词才可以发送成功,最多可以设置 10 个关键词。例如:添加了一个自定义关键词:监控报警,则这个机器人所发送的消息,必须包含 “监控报警” 这个词,才能发送成功。加签:即通过时间戳 + 密钥当做签名字符串,使用 HmacSHA256 算法计算签名,然后进行 Base64 encode,最后再把签名参数再进行 urlEncode,得到最终的签名,在发送请求时带着加密后的签名进行通信。IP 地址(段):即设定后,只有来自 IP 地址范围内的请求才会被正常处理。支持两种设置方式:IP、IP 段。本小节中采用第一种自定义关键词的形式进行添加自定义机器人小 Q,配置内容如下图所示。勾选服务条款,点击完成按钮,如下图所示。到这一步,我们就已经完成了自定义机器人的创建,但只是第一步,第二步还需要在代码中操作机器人。这里注意获取到 Webhook 地址后,用户可以向这个地址发起 HTTP 请求,即可实现给该钉钉群发送消息。Tips:在发起 HTTP 请求时,必须将字符集编码设置成 UTF-8。
- 1. Request 类 首先 Scrapy 中关于 Request 相关的源码位置如下:scrapy 中 Request 相关的代码可以看到 Request 定义相关的代码并不多,这也方便我们去学习和探索。先来看 Request 类的定义:# 源码位置:scrapy/http/request/__init__.pyfrom w3lib.url import safe_url_string# ...class Request(object_ref): def __init__(self, url, callback=None, method='GET', headers=None, body=None, cookies=None, meta=None, encoding='utf-8', priority=0, dont_filter=False, errback=None, flags=None, cb_kwargs=None): self._encoding = encoding # this one has to be set first self.method = str(method).upper() self._set_url(url) self._set_body(body) if not isinstance(priority, int): raise TypeError("Request priority not an integer: %r" % priority) self.priority = priority if callback is not None and not callable(callback): raise TypeError('callback must be a callable, got %s' % type(callback).__name__) if errback is not None and not callable(errback): raise TypeError('errback must be a callable, got %s' % type(errback).__name__) self.callback = callback self.errback = errback self.cookies = cookies or {} self.headers = Headers(headers or {}, encoding=encoding) self.dont_filter = dont_filter self._meta = dict(meta) if meta else None self._cb_kwargs = dict(cb_kwargs) if cb_kwargs else None self.flags = [] if flags is None else list(flags) # ...从上面的源码中可以看到 Scrapy 框架使用了 w3lib 模块来完成一些 Web 相关的功能,这里用到了 url 模块的相关功能。safe_url_string() 方法是将 url 转成合法的形式,也就是将一些特殊字符比如中文、空格等进行想要的编码。来看下面的例子:>>> from w3lib.url import safe_url_strin>>> url = "http://www.baidu.com/?xxx= zyz">>> safe_url_string(url)'http://www.baidu.com/?xxx=%20zyz'最后得到的 URL 形式和我们在浏览器按下 Enter 键时一致。此外,对于 Request 类实例化时可以传入多种初始属性,常用的属性含义如下:url:请求地址;method:请求类型,GET|POST|PUT|DELETE 等;callback: HTTP 请求的回调方法,用于指定该 HTTP 请求的解析响应数据的方法;headers: 设置请求头。一般而言时设置请求头的 User-Agent 字段,模拟浏览器请求;body: 用于设置请求参数,比如登录请求需要带上用户名/密码等参数;cookies: 请求 cookies 信息,一般和登录认证相关,带上 cookies 用于表明身份信息。熟悉了这个 Request 类后,我们来看一些在 Request 基础上进一步扩展的请求类。其中一个是 FormRequest:# 源码位置:scrapy/http/request/form.py# ...class FormRequest(Request): valid_form_methods = ['GET', 'POST'] def __init__(self, *args, **kwargs): formdata = kwargs.pop('formdata', None) if formdata and kwargs.get('method') is None: kwargs['method'] = 'POST' super(FormRequest, self).__init__(*args, **kwargs) if formdata: items = formdata.items() if isinstance(formdata, dict) else formdata querystr = _urlencode(items, self.encoding) if self.method == 'POST': self.headers.setdefault(b'Content-Type', b'application/x-www-form-urlencoded') self._set_body(querystr) else: self._set_url(self.url + ('&' if '?' in self.url else '?') + querystr) # ...FormRequest 类主要用于提交表单请求,比如登录认证、比如提交订单等。它只支持 GET 和 POST 请求,且相比 Request 类,FormRequest 类多了一个表单参数属性,这个是检查提交表单请求的数据。来分析实例化时对表单参数的处理,代码如下:if formdata: items = formdata.items() if isinstance(formdata, dict) else formdata querystr = _urlencode(items, self.encoding) if self.method == 'POST': self.headers.setdefault(b'Content-Type', b'application/x-www-form-urlencoded') self._set_body(querystr) else: self._set_url(self.url + ('&' if '?' in self.url else '?') + querystr) # ...def _urlencode(seq, enc): values = [(to_bytes(k, enc), to_bytes(v, enc)) for k, vs in seq for v in (vs if is_listlike(vs) else [vs])] return urlencode(values, doseq=1)这个代码的逻辑是非常清晰的,如果有表单数据,会分成 GET 和 POST 请求处理:GET 请求:将请求参数添加到 url 后面,用 “?” 连接,参数之间用 “&” 连接;POST 请求:一方面设置请求的 header,另一方面将数据放到 body 体中;还有两个 JsonRequest 和 XmlRpcRequest 类,都是使用不同的形式来发送 HTTP 请求,我们来看两个类中非常关键的几行语句:# 源码位置:scrapy/http/request/json_request.py# ...class JsonRequest(Request): def __init__(self, *args, **kwargs): # ... if body_passed and data_passed: # ... elif not body_passed and data_passed: kwargs['body'] = self._dumps(data) if 'method' not in kwargs: kwargs['method'] = 'POST' super(JsonRequest, self).__init__(*args, **kwargs) self.headers.setdefault('Content-Type', 'application/json') self.headers.setdefault('Accept', 'application/json, text/javascript, */*; q=0.01') # ...这里 JsonRequest 中主要讲 data 数据转成 json 格式,然后保存到 body 属性中,然后设置了请求头的 Content-Type 属性为 “application/json”。# 源码位置:scrapy/http/request/rpc.pyimport xmlrpc.client as xmlrpclib# ...class XmlRpcRequest(Request): def __init__(self, *args, **kwargs): # ... if 'body' not in kwargs and 'params' in kwargs: kw = dict((k, kwargs.pop(k)) for k in DUMPS_ARGS if k in kwargs) # 关键地方 kwargs['body'] = xmlrpclib.dumps(**kw) # ...XmlRpcRequest 用来发送 XML-RPC 请求,关键的地方在于请求数据设置,使用了 xmlrpc 模块。
- 1. 今日头条热点新闻数据抓取分析 今天的爬取对象是今日头条的热点新闻,下面的视频演示了如何找到头条新闻网站在获取热点新闻的 HTTP 请求:81从视频中我们可以看到头条新闻获取网站的接口示例如下:https://www.toutiao.com/api/pc/feed/?category=news_hot&utm_source=toutiao&widen=1&max_behot_time=1597152177&max_behot_time_tmp=1597152177&tadrequire=true&as=A1955F33D209BD8&cp=5F32293B3DE80E1&_signature=_02B4Z6wo0090109cl1gAAIBCcqbHy0H-dDdPWZPAAIzuFTZSh6NBsUuEpf13PktqrmxS-ZD4dEDZ6Ezcpyjo31hg62slsekkigwdRlS0FHfPsOvx.KRyeJBdEf5QI8nLcwEMyziL1YdPK6VD8f像这样的 http 请求时比较难模拟的,我们需要知道请求中所有参数的获取规则,特别是一些进行加密的方式,需要从前端中找出来并手工实现。比如这里的 URL,前几个参数都是固定值,其中 as、cp 和 _signature 则非常难获取,需要有极强的前端功底,网上也有大神对这些值的生成进行了分析和解密,当然这些不是我们学习的重点。最后一个问题:一次请求得到10条左右的新闻数据,那么像实现视频中那样更新更多新闻的请求,该如何完成呢?仔细分析下连续的刷新请求,我们会发现上述的 URL 请求结果中有这样一个参数:max_behot_time。第一次请求max_behot_time值为0next中的max_behot_time等于最后一条数据的behot_time值关于这个参数,我们得到两条信息:第一次请求热点新闻数据时,该参数为0;接下来的每次请求,带上的 max_behot_time 值为上一次请求热点新闻数据结果中的 next 字段中的 max_behot_time 键对应的值。它表示的是一个时间戳,其实就是意味着请求的热点新闻数据需要在这个时间之后;有了这样的信息,我们来基于 requests 库,纯手工实现一把头条热点新闻数据的抓取。我们按照如下的步骤来完成爬虫代码:准备基本变量,包括请求的基本 URL、请求参数、请求头等;hotnews_url = "https://www.toutiao.com/api/pc/feed/?"params = { 'category': 'news_hot', 'utm_source': 'toutiao', 'widen': 1, 'max_behot_time': '', 'max_behot_time_tmp': '',}headers = { 'referer': 'https://www.toutiao.com/ch/news_hot/', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36'}cookies = {'tt_webid':'6856365980324382215'} max_behot_time = '0'注意:上面的 cookies 中的 tt_webid 字段值可以通过右键看到,不过用处不大。tt_webid值的获取准备三个个方法:get_request_data() 、get_as_cp() 和 save_to_json()。其中第二个函数是网上有人对头条的 js 生成 as 和 cp 参数的代码进行了翻译,目前看来似乎还能使用;def get_request_data(url, headers): response = requests.get(url=url, headers=headers) return json.loads(response.text)def get_as_cp(): # 该函数主要是为了获取as和cp参数,程序参考今日头条中的加密js文件:home_4abea46.js zz = {} now = round(time.time()) e = hex(int(now)).upper()[2:] a = hashlib.md5() a.update(str(int(now)).encode('utf-8')) i = a.hexdigest().upper() if len(e) != 8: zz = {'as':'479BB4B7254C150', 'cp':'7E0AC8874BB0985'} return zz n = i[:5] a = i[-5:] r = '' s = '' for i in range(5): s = s + n[i] + e[i] for j in range(5): r = r + e[j + 3] + a[j] zz ={ 'as': 'A1' + s + e[-3:], 'cp': e[0:3] + r + 'E1' } return zzdef save_to_json(datas, file_path, key_list): """ 保存 json 数据 """ print('写入数据到文件{}中,共计{}条新闻数据!'.format(file_path, len(datas))) with codecs.open(file_path, 'a+', 'utf-8') as f: for d in datas: cleaned_data = {} for key in key_list: if key in d: cleaned_data[key] = d[key] print(json.dumps(cleaned_data, ensure_ascii=False)) f.write("{}\n".format(json.dumps(cleaned_data, ensure_ascii=False)))最后一步就是实现模拟刷新请求数据。下一次的请求会使用上一次请求结果中的 max_behot_time 值,这样能连续获取热点新闻数据,模拟头条页面向下的刷新过程;# 模拟向下下刷新5次获取新闻数据refresh_count = 5for _ in range(refresh_count): new_params = copy.deepcopy(params) zz = get_as_cp() new_params['as'] = zz['as'] new_params['cp'] = zz['cp'] new_params['max_behot_time'] = max_behot_time new_params['max_behot_time_tmp'] = max_behot_time request_url = "{}{}".format(hotnews_url, urlencode(new_params)) print(f'本次请求max_behot_time = {max_behot_time}') datas = get_request_data(request_url, headers=headers, cookies=cookies) max_behot_time = datas['next']['max_behot_time'] save_to_json(datas['data'], "result.json", key_list) time.sleep(2)最后来看看完整抓取热点新闻数据的代码运行过程,如下:82
- 2. 基于 Scrapy 框架的头条热点新闻数据爬取 还是按照我们以前的套路来进行,第一步是使用 startproject 命令创建热点新闻项目:[root@server ~]# cd scrapy-test/[root@server scrapy-test]# pyenv activate scrapy-testpyenv-virtualenv: prompt changing will be removed from future release. configure `export PYENV_VIRTUALENV_DISABLE_PROMPT=1' to simulate the behavior.(scrapy-test) [root@server scrapy-test]# scrapy startproject toutiao_hotnewsNew Scrapy project 'toutiao_hotnews', using template directory '/root/.pyenv/versions/3.8.1/envs/scrapy-test/lib/python3.8/site-packages/scrapy/templates/project', created in: /root/scrapy-test/toutiao_hotnewsYou can start your first spider with: cd toutiao_hotnews scrapy genspider example example.com(scrapy-test) [root@server scrapy-test]#接着,根据我们要抓取的新闻数据字段,先定义好 Item:import scrapyclass ToutiaoHotnewsItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() title = scrapy.Field() abstract = scrapy.Field() source = scrapy.Field() source_url = scrapy.Field() comments_count = scrapy.Field() behot_time = scrapy.Field()有了 Item 之后,我们需要新建一个 Spider,可以使用 genspider 命令生成,也可以手工编写一个 Python 文件,代码内容如下:# 代码位置:toutiao_hotnews/toutiao_hotnews/spiders/hotnews.pyimport copyimport hashlibfrom urllib.parse import urlencodeimport jsonimport timefrom scrapy import Request, Spiderfrom toutiao_hotnews.items import ToutiaoHotnewsItemhotnews_url = "https://www.toutiao.com/api/pc/feed/?"params = { 'category': 'news_hot', 'utm_source': 'toutiao', 'widen': 1, 'max_behot_time': '', 'max_behot_time_tmp': '', 'as': '', 'cp': ''}headers = { 'referer': 'https://www.toutiao.com/ch/news_hot/', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36'}cookies = {'tt_webid':'6856365980324382215'} max_behot_time = '0'def get_as_cp(): # 该函数主要是为了获取as和cp参数,程序参考今日头条中的加密js文件:home_4abea46.js zz = {} now = round(time.time()) e = hex(int(now)).upper()[2:] a = hashlib.md5() a.update(str(int(now)).encode('utf-8')) i = a.hexdigest().upper() if len(e) != 8: zz = {'as':'479BB4B7254C150', 'cp':'7E0AC8874BB0985'} return zz n = i[:5] a = i[-5:] r = '' s = '' for i in range(5): s = s + n[i] + e[i] for j in range(5): r = r + e[j + 3] + a[j] zz ={ 'as': 'A1' + s + e[-3:], 'cp': e[0:3] + r + 'E1' } return zzclass HotnewsSpider(Spider): name = 'hotnews' allowed_domains = ['www.toutiao.com'] start_urls = ['http://www.toutiao.com/'] # 记录次数,注意停止 count = 0 def _get_url(self, max_behot_time): new_params = copy.deepcopy(params) zz = get_as_cp() new_params['as'] = zz['as'] new_params['cp'] = zz['cp'] new_params['max_behot_time'] = max_behot_time new_params['max_behot_time_tmp'] = max_behot_time return "{}{}".format(hotnews_url, urlencode(new_params)) def start_requests(self): """ 第一次爬取 """ request_url = self._get_url(max_behot_time) self.logger.info(f"we get the request url : {request_url}") yield Request(request_url, headers=headers, cookies=cookies, callback=self.parse) def parse(self, response): """ 根据得到的结果得到获取下一次请求的结果 """ self.count += 1 datas = json.loads(response.text) data = datas['data'] for d in data: item = ToutiaoHotnewsItem() item['title'] = d['title'] item['abstract'] = d.get('abstract', '') item['source'] = d['source'] item['source_url'] = d['source_url'] item['comments_count'] = d.get('comments_count', 0) item['behot_time'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(d['behot_time'])) self.logger.info(f'得到的item={item}') yield item if self.count < self.settings['REFRESH_COUNT']: max_behot_time = datas['next']['max_behot_time'] self.logger.info("we get the next max_behot_time: {}, and the count is {}".format(max_behot_time, self.count)) yield Request(self._get_url(max_behot_time), headers=headers, cookies=cookies)这里的代码之前一样,第一次构造 Request 请求在 start_requests() 方法中,接下来在根据每次请求结果中获取 max_behot_time 值再进行下一次请求。另外我使用了全局计算变量 count 来模拟刷新的次数,控制请求热点新闻次数,防止无限请求下去。此外,Scrapy logger 在每个 spider 实例中提供了一个可以访问和使用的实例,我们再需要打印日志的地方直接使用 self.logger 即可,它对应日志的配置如下:# 代码位置:toutiao_hotnews/settings.py# 注意设置下下载延时DOWNLOAD_DELAY = 5# ...#是否启动日志记录,默认TrueLOG_ENABLED = True LOG_ENCODING = 'UTF-8'#日志输出文件,如果为NONE,就打印到控制台LOG_FILE = 'toutiao_hotnews.log'#日志级别,默认DEBUGLOG_LEVEL = 'INFO'# 日志日期格式 LOG_DATEFORMAT = "%Y-%m-%d %H:%M:%S"#日志标准输出,默认False,如果True所有标准输出都将写入日志中,比如代码中的print输出也会被写入到LOG_STDOUT = False接下来是 Item Pipelines 部分,这次我们将抓取到的新闻保存到 MySQL 数据库中。此外,我们还有一个需求就是选择当前最新的10条新闻发送到本人邮件,这样每天早上就能定时收到最新的头条新闻,岂不美哉。首先我想给自己的邮件发送 HTML 格式的数据,然后列出最新的10条新闻,因此第一步是是准备好模板热点新闻的模板页面,具体模板页面如下:# 代码位置: toutiao_hotnews/html_template.pyhotnews_template_html = """<!DOCTYPE html><html><head> <title>头条热点新闻一览</title></head><style type="text/css"></style><body><div class="container"><h3 style="margin-bottom: 10px">头条热点新闻一览</h3>$news_list</div></body></html>"""要注意一点,Scrapy 的邮箱功能只能发送文本内容,不能发送 HTML 内容。为了能支持发送 HTML 内容,我继承了原先的 MailSender 类,并对原先的 send() 方法稍做改动:# 代码位置: mail.pyimport logging from email import encoders as Encodersfrom email.mime.base import MIMEBasefrom email.mime.multipart import MIMEMultipartfrom email.mime.nonmultipart import MIMENonMultipartfrom email.mime.text import MIMETextfrom email.utils import COMMASPACE, formatdatefrom scrapy.mail import MailSenderfrom scrapy.utils.misc import arg_to_iterlogger = logging.getLogger(__name__)class HtmlMailSender(MailSender): def send(self, to, subject, body, cc=None, mimetype='text/plain', charset=None, _callback=None): from twisted.internet import reactor #####去掉了与attachs参数相关的判断语句,其余代码不变############# msg = MIMEText(body, 'html', 'utf-8') ########################################################## to = list(arg_to_iter(to)) cc = list(arg_to_iter(cc)) msg['From'] = self.mailfrom msg['To'] = COMMASPACE.join(to) msg['Date'] = formatdate(localtime=True) msg['Subject'] = subject rcpts = to[:] if cc: rcpts.extend(cc) msg['Cc'] = COMMASPACE.join(cc) if charset: msg.set_charset(charset) if _callback: _callback(to=to, subject=subject, body=body, cc=cc, attach=attachs, msg=msg) if self.debug: logger.debug('Debug mail sent OK: To=%(mailto)s Cc=%(mailcc)s ' 'Subject="%(mailsubject)s" Attachs=%(mailattachs)d', {'mailto': to, 'mailcc': cc, 'mailsubject': subject, 'mailattachs': len(attachs)}) return dfd = self._sendmail(rcpts, msg.as_string().encode(charset or 'utf-8')) dfd.addCallbacks( callback=self._sent_ok, errback=self._sent_failed, callbackArgs=[to, cc, subject, len(attachs)], errbackArgs=[to, cc, subject, len(attachs)], ) reactor.addSystemEventTrigger('before', 'shutdown', lambda: dfd) return dfd紧接着就是我们的 pipelines.py 文件中的代码:import loggingfrom string import Templatefrom itemadapter import ItemAdapterimport pymysqlfrom toutiao_hotnews.mail import HtmlMailSenderfrom toutiao_hotnews.items import ToutiaoHotnewsItemfrom toutiao_hotnews.html_template import hotnews_template_htmlfrom toutiao_hotnews import settingsclass ToutiaoHotnewsPipeline: logger = logging.getLogger('pipelines_log') def open_spider(self, spider): # 使用自己的MailSender类 self.mailer = HtmlMailSender().from_settings(spider.settings) # 初始化连接数据库 self.db = pymysql.connect( host=spider.settings.get('MYSQL_HOST', 'localhost'), user=spider.settings.get('MYSQL_USER', 'root'), password=spider.settings.get('MYSQL_PASS', '123456'), port=spider.settings.get('MYSQL_PORT', 3306), db=spider.settings.get('MYSQL_DB_NAME', 'mysql'), charset='utf8' ) self.cursor = self.db.cursor() def process_item(self, item, spider): # 插入sql语句 sql = "insert into toutiao_hotnews(title, abstract, source, source_url, comments_count, behot_time) values (%s, %s, %s, %s, %s, %s)" if item and isinstance(item, ToutiaoHotnewsItem): self.cursor.execute(sql, (item['title'], item['abstract'], item['source'], item['source_url'], item['comments_count'], item['behot_time'])) return item def query_data(self, sql): data = {} try: self.cursor.execute(sql) data = self.cursor.fetchall() except Exception as e: logging.error('database operate error:{}'.format(str(e))) self.db.rollback() return data def close_spider(self, spider): sql = "select title, source_url, behot_time from toutiao_hotnews where 1=1 order by behot_time limit 10" # 获取10条最新的热点新闻 data = self.query_data(sql) news_list = "" # 生成html文本主体 for i in range(len(data)): news_list += "<div><span>{}、<a href=https://www.toutiao.com{}>{} [{}]</a></span></div>".format(i + 1, data[i][1], data[i][0], data[i][2]) msg_content = Template(hotnews_template_html).substitute({"news_list": news_list}) self.db.commit() self.cursor.close() self.db.close() self.logger.info("最后统一发送邮件") # 必须加return,不然会报错 return self.mailer.send(to=["2894577759@qq.com"], subject="这是一个测试", body=msg_content, cc=["2894577759@qq.com"])这里我们会将 MySQL 的配置统一放到 settings.py 文件中,然后使用 spider.settings 来读取响应的信息。其中 open_spider() 方法用于初始化连接数据库,process_item() 方法用于生成 SQL 语句并提交插入动作,最后的 close_spider() 方法用于提交数据库执行动作、关闭数据库连接以及发送统一新闻热点邮件。下面是我们将这个 Pipeline 在 settings.py 中开启以及配置数据库信息、邮件服务器信息,同时也要注意关闭遵守 Robot 协议,这样爬虫才能正常执行。ROBOTSTXT_OBEY = False# 启动对应的pipelineITEM_PIPELINES = { 'toutiao_hotnews.pipelines.ToutiaoHotnewsPipeline': 300,}# 数据库配置MYSQL_HOST = "180.76.152.113"MYSQL_PORT = 9002MYSQL_USER = "store"MYSQL_PASS = "数据库密码"MYSQL_DB_NAME = "ceph_check"# 邮箱配置MAIL_HOST = 'smtp.qq.com'MAIL_PORT = 25MAIL_FROM = '2894577759@qq.com'MAIL_PASS = '你的授权码'MAIL_USER = '2894577759@qq.com'来看看我们这个头条新闻爬虫的爬取效果,视频演示如下:83
urlencode相关搜索
-
ubuntu安装
ubuntu安装教程
ubuntu官网
ucenter
udp通信
ui层
uml
uml类图
uml建模
uml教程
uml图
underscore
uni app
unicode
uniform
union
union用法
uniq
uniqueconstraint
uniqueid