comments相关知识
-
MySQL字段自增自减的SQL语句示例介绍 MySQL的自增语句大家应该都很熟悉 也很简单 复制代码 代码如下: update `info` set `comments` = `comments`+1 WHERE `id` = 32 这样就可以了,但是有时候我们会涉及到做减法, 例如:文章的评论数,在删除或者锁定了一条评论之后需要对该文章总评论数减一 comments smallint(5) unsigned 文章评论总数统计字段 无符号即 0 ~ 65535 之间的数值 1. 通常情况下是可以类似上面自增的方法 把 +号 改成 -号 就行了,但问题是如果当前 comments 统计数值为 0 时 再做减法将会变成该字段类型的最大数值 65535 复制代码 代码如下: update `info` set `comments` = `comments`-1 WHERE `id` = 32 2. 为避免这个问题一般的想法只能是先根据 id 主键查询出文章 comment
-
五十二章 TP51之关联模型一对多关联<?phpnamespace app\index\model;use think\Model;class Article extends Model { public function comments() { return $this->hasMany('Comment','art_id','id');}}}关联查询我们可以通过下面的方式获取关联数据$article = Article::get(1);// 获取文章的所有评论dump($article->comments);// 也可以进行条件搜索dump($article->comments()->where('status',1)->select());根据关联条件查询可以根据关联条件来查询当前模型对象数据,例如:// 查询评论超过3个的
-
java学习第二季的项目代码java学习第二季的项目代码 Vehicle类。是bus、truck、pickup的父类 package pmy.test; /* *Project: Lease System *Comments: 汽车类,客车、火车、皮卡类的基类 *JDK version used: JDK1.8 *Author: Pmy *Create Date: 2016-09-13 */ public class Vehicle { private String s_name; private int i_price; public void setName(String name){ this.s_name = name; } public String getName(){ return s_name; } public void setPrice(int price
-
Python练手,numpy.genfromtxt# -*- coding: utf-8 -*- '''about numpy.genfromtxt, means generate from txt file https://docs.scipy.org/doc/numpy/reference/generated/numpy.genfromtxt.html numpy.genfromtxt(fname, dtype=<type 'float'>, comments='#', delimiter=None, skip_header=0, skip_footer=0, converters=None, missing_values=None, filling_values=No
comments相关课程
comments相关教程
- 3.1 单行注释 单行注释是在 // 后面跟你的注释内容,直到行尾,要记住 Sass 中的单行注释不会被编译到 CSS 中,也就是说在转换为 CSS 代码后,是不会包含你写的单行注释的!我们举个例子来看下:// This is a comments// This is a comments.box2 { width: 200px; height: 100px; p { color: red } .main { width: 100%; }}上面的代码转换为 CSS 如下:.box2 { width: 200px; height: 100px;}.box2 p { color: red;}.box2 .main { width: 100%;}从上面的代码中我们可以看出,转换为 CSS 后我们在 Sass 中写的单行注释消失了。
- 2. Scrapy 与 Selenium 结合爬取京东图书数据 接下来我们对上面的代码进行调整和 Scrapy 框架结合,而第一步需要做的就是建立好相应的工程:# 创建爬虫项目PS D:\shencong\scrapy-lessons\code\chap17> scrapy startproject jdbooks# ...# 进入到spider目录,使用genspider命令创建爬虫文件PS D:\shencong\scrapy-lessons\code\chap17\jd_books\jd_books\spiders> scrapy genspider jd www.jd.com创建好工程后就是编写 items.py 中的 JdBooksItem 类,这非常简单,直接根据我们前面定义好的字段编写相应的代码即可:class JdBooksItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() book_name = scrapy.Field() price = scrapy.Field() comments = scrapy.Field() shop_name = scrapy.Field() book_detail_url = scrapy.Field()整个项目的难点是如何实现下一页数据的爬取?前面可以使用 selenium 去自动点击页号而进入下一个,然而在 Scrapy 中却不太好这样处理。我们通过分析京东搜索的 URL 后发现,其搜索的 URL 可以简化为如下形式:https://search.jd.com/Search?keyword=搜索关键字&page=(页号* 2 - 1),我们只需要提供搜索的关键字以及相应的请求页号即可。例如下图所示:京东搜索 URL 参数因此我们在 settings.py 中准备两个参数:一个是搜索的关键字,另一个是爬取的最大页数。具体的形式如下:# settings.py# ...KEYWORD = "网络爬虫"MAX_PAGE = 10紧接着我们可以构造出请求不同页的 URL 并交给 Scrapy 的引擎和调度器去处理,对应的 Spider 代码如下:# 代码位置:jd_books/jd_books/spiders/jd.pyfrom urllib.parse import quotefrom scrapy import Spider, Requestfrom jd_books.items import JdBooksItemclass JdSpider(Spider): name = 'jd' allowed_domains = ['www.jd.com'] start_urls = ['http://www.jd.com/'] base_url = "https://search.jd.com/Search?keyword={}&page={}" def start_requests(self): keyword = self.settings.get('KEYWORD', "Python") for page in range(1, self.settings.get('MAX_PAGE') + 1): url = self.base_url.format(quote(keyword), page * 2 - 1) yield Request(url=url, callback=self.parse_books, dont_filter=True) def parse_books(self, response): goods_list = response.xpath('//div[@id="J_goodsList"]/ul/li') print('本页获取图书数目:{}'.format(len(goods_list))) for good in goods_list: book_name_em = good.xpath('.//div[@class="p-name"]/a/em/text()').extract()[0] book_name_font = good.xpath('.//div[@class="p-name"]/a/em/font/text()').extract() book_name_font = "".join(book_name_font) if book_name_font else "" book_name = f"{book_name_em}{book_name_font}" book_detail_url = good.xpath('.//div[@class="p-name"]/a/@href').extract()[0] price = good.xpath('.//div[@class="p-price"]/strong/i/text()').extract()[0] comments = good.xpath('.//div[@class="p-commit"]/strong/a/text()').extract()[0] shop_name = good.xpath('.//div[@class="p-shopnum"]/a/text()').extract()[0] item = JdBooksItem() item['book_name'] = book_name item['book_detail_url'] = book_detail_url item['price'] = price item['comments'] = comments item['shop_name'] = shop_name yield item上面的代码就是单纯的生成多页的 Request 请求 (start_requests() 方法) 和解析网页数据 (parse_books() 方法)。这个解析数据完全依赖于我们获取完整的页面源码,那么如何在 Scrapy 中使用 selenium 去请求 URL 然后获取页面源码呢?答案就是下载中间件。我们在编写一个下载中间件,拦截发送的 request 请求,对于请求京东图书数据的请求我们会切换成 selenium 的方式去获取网页源码,然后将得到的页面源码封装成 Response 响应并返回。在生成 Scrapy 项目中已经为我们准备好了一个 middleware.py 文件,我们按照上面的思路来完成相应代码,具体内容如下:import timefrom scrapy import signalsfrom scrapy.http.response.html import HtmlResponsefrom selenium import webdriverfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.common.by import By# useful for handling different item types with a single interfacefrom itemadapter import is_item, ItemAdapteroptions = webdriver.ChromeOptions()# 注意,使用这个参数我们就不会看到启动的google浏览器,无界面运行options.add_argument('-headless')options.add_experimental_option("excludeSwitches", ['enable-automation'])class JdBooksSpiderMiddleware: # 保持不变 # ... class JdBooksDownloaderMiddleware: # Not all methods need to be defined. If a method is not defined, # scrapy acts as if the downloader middleware does not modify the # passed objects. def __init__(self): self.driver = webdriver.Chrome(options=options, executable_path="C:/Users/Administrator/AppData/Local/Google/Chrome/Application/chromedriver.exe") # ... def process_request(self, request, spider): # Called for each request that goes through the downloader # middleware. # Must either: # - return None: continue processing this request # - or return a Response object # - or return a Request object # - or raise IgnoreRequest: process_exception() methods of # installed downloader middleware will be called print('使用 selenium 请求页面:{}'.format(request.url)) if request.url.startswith("https://search.jd.com/Search"): # 如果是获取京东图书数据的请求,使用selenium方式获取页面 self.driver.get(request.url) time.sleep(2) # 将滚动条拖到最底端,获取一页完整的60条数据 height = self.driver.execute_script("return document.body.scrollHeight;") self.driver.execute_script(f"window.scrollBy(0, {height})") time.sleep(2) # 将最后渲染得到的页面源码作为响应返回 return HtmlResponse(url=request.url, body=self.driver.page_source, request=request, encoding='utf-8', status=200) # ...紧接着,我们需要将这个下载中间件在 settings.py 中启用:DOWNLOADER_MIDDLEWARES = { 'jd_books.middlewares.JdBooksDownloaderMiddleware': 543,}最后我们来完成下数据的存储,继续使用 mongodb 来保存抓取到的数据。从实际测试中发现京东的搜索结果在100页中肯定会有不少重复的数据。因此我们的 item pipelines 需要完成2个处理,分别是去重和保存。来直接看代码:import pymongofrom scrapy.exceptions import DropItemfrom itemadapter import ItemAdapterclass JdBooksPipeline: def open_spider(self, spider): self.client = pymongo.MongoClient(host='47.115.61.209', port=27017) self.client.admin.authenticate("admin", "shencong1992") db = self.client.scrapy_manual self.collection = db.jd_books def process_item(self, item, spider): try: book_info = { 'book_name': item['book_name'], 'comments': item['comments'], 'book_detail_url': item['book_detail_url'], 'shop_name': item['shop_name'], 'price': item['price'], } self.collection.insert_one(book_info) except Exception as e: print("插入数据异常:{}".format(str(e))) return item def close_spider(self, spider): self.client.close()class DuplicatePipeline: """ 去除重复的数据,重复数据直接抛出异常,不会进入下一个流水线处理 """ def __init__(self): self.book_url_set = set() def process_item(self, item, spider): if item['book_detail_url'] in self.book_url_set: print('重复搜索结果:book={}, url={}'.format(item['book_name'], item['book_detail_url'])) raise DropItem('duplicate book info, drop it') self.book_url_set.add(item['book_detail_url']) return item我们直接使用 Item 的 book_detail_url 字段来判断数据是否重复。此外,同样需要将这两个 Item Pipelines 在 settings.py 中启用,且保证 DuplicatePipeline 需要先于 JdBooksPipeline 处理:ITEM_PIPELINES = { 'jd_books.pipelines.DuplicatePipeline': 200, 'jd_books.pipelines.JdBooksPipeline': 300,}最后剩下一步就是禁止遵守 Robot 协议:ROBOTSTXT_OBEY = True至此,我们的 Scrapy 和 Selenium 结合爬取京东图书数据的项目就算完成了。为了快速演示效果,我们将最大请求页设置为10,然后运行代码看看实际的爬取效果:107
- 3. 再战今日头条热点新闻爬取 上一部分我们费了九牛二虎之力爬取的头条热点新闻,今天要使用 scrapy-splash 插件和 splash 服务轻轻松松完成。我们省去前面创建 scrapy 爬虫的过程,直接看重点:首先是定义 items:import scrapyclass ToutiaoSpiderItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() title = scrapy.Field() source = scrapy.Field() comments = scrapy.Field() passed_time = scrapy.Field()然后是爬虫的核心代码:from scrapy import Spiderfrom scrapy_splash.request import SplashRequestfrom toutiao_spider.items import ToutiaoSpiderItemscript = """function main(splash, args) assert(splash:go(args.url)) splash:wait(2) return { html = splash:html(), png = splash:png(), har = splash:har(), }end"""class TouTiaoSpider(Spider): name = "toutiao_spider" def start_requests(self): splah_args = { "lua_source": script, # 这个非常重要 'wait': 5, } headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36' } yield SplashRequest(url="https://www.toutiao.com/ch/news_hot/", args=splah_args, headers=headers) def parse(self, response): news_list = response.css('div.wcommonFeed ul li') print("共{}条数据".format(len(news_list))) for news in news_list: title = news.css('div.title-box a::text').extract_first() source = news.css('div.footer a:nth-child(2)::text').extract_first() comments = news.css('div.footer a:nth-child(3)::text').extract_first() passed_time = news.css('div.footer span::text').extract_first() if not title: continue items = ToutiaoSpiderItem() items['title'] = title.strip() # 使用split()再join()的方式是为了清除前后的\xa0 items['source'] = "".join(source.split()) items['comments'] = "".join(comments.split()) items['passed_time'] = "".join(passed_time.split()) print(f'抓取数据到:{items}') # yield items这里的代码相比 Scrapy 代码变化的只有一个地方,就是对应生成的 Request 请求,我们需要替换成 scrapy-splash 插件中的 SplashRequest 类。在该类中最重要的就是 args 参数,这里我们会带上相应的 lua 执行脚本,也就是前面 Splash 服务的网页上看到的那个脚本。此外,这里我换成了 CSS 选择器去解析网页数据,其实和 xpath 方式并没有什么不同;最后来看 settings.py 中的配置,和官方推荐的方式保持一致即可,不过我做了一些改动:指定 SPLASH_URL,即 Splash 服务地址,这里对应的值为 http://47.115.61.209:8050/;添加 scrapy-splash 的中间件:SPIDER_MIDDLEWARES = { 'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,}添加 scrapy-splash 的下载中间件:DOWNLOADER_MIDDLEWARES = { 'scrapy_splash.SplashCookiesMiddleware': 723, 'scrapy_splash.SplashMiddleware': 725, 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810, }另外,scrapy-splash 也提供了一个重复过滤器类:DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'不过这样爬取时我们遇到了如下的报错:2020-08-02 22:07:26 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.toutiao.com/robots.txt> (referer: None)2020-08-02 22:07:26 [scrapy.downloadermiddlewares.robotstxt] DEBUG: Forbidden by robots.txt: <GET https://www.toutiao.com/ch/news_hot/>2020-08-02 22:07:26 [scrapy.core.engine] INFO: Closing spider (finished)头条网站使用 robots 协议禁止我们爬取它的网站,当然我们只需要无视这样的协议,继续执行爬取动作即可。在 settings.py 中将遵守 robots 协议的开关禁止即可:# Obey robots.txt rulesROBOTSTXT_OBEY = False来看看最后的执行效果:PS D:\shencong\scrapy-lessons\code\chap16\toutiao_spider> scrapy crawl toutiao_spider101给大家留一个课后作业:今日头条的新闻是需要鼠标向下滑动,然后才会加载更多热点新闻,那么我们如何利用这个 Splash 服务来能实现抓取更多的头条热点数据呢?
- 1. 需求分析与初步实现 今天我们的目的是使用 Scrapy 和 Selenium 结合来爬取京东商城中搜索 “网络爬虫” 得到的所有图书数据,类似于下面这样的数据:京东商城搜索 网络爬虫搜索出的结果有9800+条数据,共计100页。我们现在要抓取所有的和网络爬虫相关的书籍数据。有一个问题需要注意,搜索的100页数据中必定存在重复的结果,我们可以依据图书的详细地址来进行去重。此外,我们提取的图书数据字段有:图书名;价格;评价数;店铺名称;图书详细地址;需求已经非常明确,现在开始使用 Selenium 和 Scrapy 框架结合来完成这一需求。来看看如果我们是单纯使用 Selenium 工具,该如何完成数据爬取呢?这里会有一个问题需要注意:按下搜索按钮后,显示的数据只有30条,只有使用鼠标向下滚动后,才会加载更多数据,最终显示60条结果,然后才会到达翻页的地方。在 selenium 中我们可以使用如下两行代码实现滚动条滑到最底端:height = driver.execute_script("return document.body.scrollHeight;")driver.execute_script(f"window.scrollBy(0, {height})")time.sleep(2)可以看到,上面两行代码主要是执行 js 语句。第一行代码是得到页面的底部位置,第二行代码是使用 scrollBy() 方法控制页面滚动条移动到底部。接下来,我们来看看页面数据的提取,直接右键 F12,可以通过 xpath 表达式得到所有需要抓取的数据。为此,我编写了一个根据页面代码提取图书数据的方法,具体如下:def parse_book_data(html): etree_html = etree.HTML(html) # 获取列表 gl_items = etree_html.xpath('//div[@id="J_goodsList"]/ul/li') print('总共获取数据:{}'.format(len(gl_items))) res = [] for item in gl_items: book_name_em = item.xpath('.//div[@class="p-name"]/a/em/text()')[0] book_name_font = item.xpath('.//div[@class="p-name"]/a/em/font/text()') book_name_font = "".join(book_name_font) if book_name_font else "" # 获取图书名 book_name = f"{book_name_em}{book_name_font}" # 获取图书的详细介绍地址 book_detail_url = item.xpath('.//div[@class="p-name"]/a/@href')[0] # 获取图书价格 price = item.xpath('.//div[@class="p-price"]/strong/i/text()')[0] # 获取评论数 comments = item.xpath('.//div[@class="p-commit"]/strong/a/text()')[0] # 获取店铺名称 shop_name = item.xpath('.//div[@class="p-shopnum"]/a/text()') shop_name = shop_name[0] if shop_name else "" data = {} data['book_name'] = book_name data['book_detail_url'] = book_detail_url data['price'] = price data['comments'] = comments data['shop_name'] = shop_name res.append(data) # 返回页面解析的结果 print('本页获取的结果:{}'.format(res)) return res现在来思考下如何能使用 selenium 一页一页访问?我给出了如下代码:def get_page_data(driver, page): """ :driver 驱动 :page 第几页 """ # 请求当前页 if page > 1: WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.ID, 'J_bottomPage')) ) driver.find_element_by_xpath(f'//div[@id="J_bottomPage"]/span/a[text()="{page}"]').click() time.sleep(2) # 滚动到最下面,出现京东图书剩余书籍数据 height = driver.execute_script("return document.body.scrollHeight;") driver.execute_script(f"window.scrollBy(0, {height})") time.sleep(2) return parse_book_data(driver.page_source)对于第一页的访问是在输入关键字<网络爬虫>后点击按钮得到的,我们不需要放到这个函数来得到,只需要滚动到底部得到所有的图书数据即可;而对于第2页之后的页面,我们需要使用 selenium 的模拟鼠标点击功能,点击下对应页后便能跳转得到该页,然后再滚动到底部,就可以得到整页的搜索结果。我们来看看完整的实现:import timeimport randomimport refrom selenium import webdriverfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.common.by import Byfrom selenium.webdriver import ActionChainsfrom lxml import etreedef get_page_data(driver, page): """ :driver 驱动 :page 第几页 """ # 具体代码参考上面 # ... def parse_book_data(html): """ 解析页面图书数据 """ # 具体代码参考上面 # ... options = webdriver.ChromeOptions()options.add_experimental_option("excludeSwitches", ['enable-automation'])driver = webdriver.Chrome(options=options, executable_path="C:/Users/Administrator/AppData/Local/Google/Chrome/Application/chromedriver.exe")driver.maximize_window()driver.get("https://www.jd.com/")# 输入网络爬虫,然后点击搜索driver.find_element_by_id('key').send_keys('网络爬虫')driver.find_elements_by_xpath('//div[@role="serachbox"]/button')[0].click()time.sleep(2)max_page = 100for i in range(1, max_page + 1): get_page_data(driver, i)下面来看看代码执行的效果,这里为了能尽快执行完,我将 max_page 参数调整为10,只获取10页搜索结果,一共是600条数据:106从上面的演示中,可以看到最后每页抓取的数据都是60条。
- 2. IETF RFC TCP/IP 标准都是以 RFC(Request for Comments)文档出版。RFC 格式诞生于 1969年,是 ARPANET 项目的开创性成果。如今 RFC 已经是 TCP/IP 标准的的官方发行渠道。请看下图来了解 RFC 的主要来源机构。互联网工程任务组 IETF(Internet Engineering Task Force)是一个开放标准组织,主要工作是开发和推广自愿的 Internet 标准,专注于工程和标准制定的短期问题研究。互联网研究任务组 IRTF(Internet Research Task Force)是一个专注于和 Internet 相关的、长期问题的研究。互联网体系结构委员会 IAB(Internet Architecture Board)是 IETF 的委员会,也是 ISOC 的咨询机构。其职责是对 IETF 活动进行监督。互联网协会 ISOC(Internet Society)是成立于 1992 年的美国非营利性组织,领导着互联网标准的推广、教育、政策方面工作。RFC 主要是由工程师和计算机科学家以备忘录的形式撰写的。很多 RFC 是实验性的,并不是标准,IETF 会吸收一些 RFC 建议,然后将其作为标准发布。RFC 标准都是以 "RFC xxx"的形式发布的,xxx 是编号,比如 RFC 1122。RFC 经过几十年的发展,到写作此文为止,最新编号是 RFC 8900。关于 RFC 的索引库,可以参考 rfc index。IETF 主要涉及的领域:Internet 主要是 IP 层相关协议的开发、扩展。比如,IPv6、DHCP 等。Routing 负主要是 IP 路由相协议的开发。比如,MPLS、BGP、OSPF 等。Transport 主要是 QoS、端到端传输相关协议开发。Security 主要是网络安全相关协议开发。Network O&M(Operations And Management)主要是网络服务、管理相关工作,比如,SNMP、MIB 等。User Services 主要是提供标准化过程中的文档。Application 主要是应用协议开发和扩展。比如,FTP、SMTP、TELNET。
- Netty粘包和拆包 一套适合初学者学习的 Netty 网络通信教程
comments相关搜索
-
c 正则表达式
c string
c 编程
c 程序设计
c 程序设计教程
c 多线程编程
c 教程
c 数组
c 委托
c 下载
c 线程
c 语言
caidan
cakephp
call
calloc
calu
camera
caption
case语句