Python 爬虫下一代网络请求库 httpx 和 parsel 解析库测评

2023-05-16

这是「进击的Coder」的第 437 篇技术分享

作者:大江狗

来源:Python Web与Django开发

阅读本文大概需要 8 分钟。

Python 网络爬虫领域两个最新的比较火的工具莫过于 httpx 和 parsel 了。httpx 号称下一代的新一代的网络请求库,不仅支持 requests 库的所有操作,还能发送异步请求,为编写异步爬虫提供了便利。parsel 最初集成在著名 Python 爬虫框架 Scrapy 中,后独立出来成立一个单独的模块,支持 XPath 选择器, CSS 选择器和正则表达式等多种解析提取方式, 据说相比于 BeautifulSoup,parsel 的解析效率更高。

今天我们就以爬取链家网上的二手房在售房产信息为例,来测评下 httpx 和 parsel 这两个库。为了节约时间,我们以爬取上海市浦东新区 500 万元 -800 万元以上的房产为例。

requests + BeautifulSoup 组合

首先上场的是 Requests + BeautifulSoup 组合,这也是大多数人刚学习 Python 爬虫时使用的组合。本例中爬虫的入口 url 是https://sh.lianjia.com/ershoufang/pudong/a3p5/, 先发送请求获取最大页数,然后循环发送请求解析单个页面提取我们所要的信息(比如小区名,楼层,朝向,总价,单价等信息),最后导出csv文件。如果你正在阅读本文,相信你对Python 爬虫已经有了一定了解,所以我们不会详细解释每一行代码。

整个项目代码如下所示:

# homelink_requests.py
# Author: 大江狗
 from fake_useragent import UserAgent
 import requests
 from bs4 import BeautifulSoup
 import csv
 import re
 import time




 class HomeLinkSpider(object):
     def __init__(self):
         self.ua = UserAgent()
         self.headers = {"User-Agent": self.ua.random}
         self.data = list()
         self.path = "浦东_三房_500_800万.csv"
         self.url = "https://sh.lianjia.com/ershoufang/pudong/a3p5/"


     def get_max_page(self):
         response = requests.get(self.url, headers=self.headers)
         if response.status_code == 200:
             soup = BeautifulSoup(response.text, 'html.parser')
             a = soup.select('div[class="page-box house-lst-page-box"]')
             #使用eval是字符串转化为字典格式
             max_page = eval(a[0].attrs["page-data"])["totalPage"] 
             return max_page
         else:
             print("请求失败 status:{}".format(response.status_code))
             return None


     def parse_page(self):
         max_page = self.get_max_page()
         for i in range(1, max_page + 1):
             url = 'https://sh.lianjia.com/ershoufang/pudong/pg{}a3p5/'.format(i)
             response = requests.get(url, headers=self.headers)
             soup = BeautifulSoup(response.text, 'html.parser')
             ul = soup.find_all("ul", class_="sellListContent")
             li_list = ul[0].select("li")
             for li in li_list:
                 detail = dict()
                 detail['title'] = li.select('div[class="title"]')[0].get_text()


                 #  2室1厅 | 74.14平米 | 南 | 精装 | 高楼层(共6层) | 1999年建 | 板楼
                 house_info = li.select('div[class="houseInfo"]')[0].get_text()
                 house_info_list = house_info.split(" | ")


                 detail['bedroom'] = house_info_list[0]
                 detail['area'] = house_info_list[1]
                 detail['direction'] = house_info_list[2]


                 floor_pattern = re.compile(r'\d{1,2}')
                 # 从字符串任意位置匹配
                 match1 = re.search(floor_pattern, house_info_list[4])  
                 if match1:
                     detail['floor'] = match1.group()
                 else:
                     detail['floor'] = "未知"


                 # 匹配年份
                 year_pattern = re.compile(r'\d{4}')
                 match2 = re.search(year_pattern, house_info_list[5])
                 if match2:
                     detail['year'] = match2.group()
                 else:
                     detail['year'] = "未知"


                 # 文兰小区 - 塘桥, 提取小区名和哈快
                 position_info = li.select('div[class="positionInfo"]')[0].get_text().split(' - ')
                 detail['house'] = position_info[0]
                 detail['location'] = position_info[1]


                 # 650万,匹配650
                 price_pattern = re.compile(r'\d+')
                 total_price = li.select('div[class="totalPrice"]')[0].get_text()
                 detail['total_price'] = re.search(price_pattern, total_price).group()


                 # 单价64182元/平米, 匹配64182
                 unit_price = li.select('div[class="unitPrice"]')[0].get_text()
                 detail['unit_price'] = re.search(price_pattern, unit_price).group()
                 self.data.append(detail)


     def write_csv_file(self):
         head = ["标题", "小区", "房厅", "面积", "朝向", "楼层", "年份",
         "位置", "总价(万)", "单价(元/平方米)"]
         keys = ["title", "house", "bedroom", "area", "direction",
         "floor", "year", "location",
                 "total_price", "unit_price"]


         try:
             with open(self.path, 'w', newline='', encoding='utf_8_sig') as csv_file:
                 writer = csv.writer(csv_file, dialect='excel')
                 if head is not None:
                     writer.writerow(head)
                 for item in self.data:
                     row_data = []
                     for k in keys:
                         row_data.append(item[k])
                         # print(row_data)
                     writer.writerow(row_data)
                 print("Write a CSV file to path %s Successful." % self.path)
         except Exception as e:
             print("Fail to write CSV to path: %s, Case: %s" % (self.path, e))


 if __name__ == '__main__':
     start = time.time()
     home_link_spider = HomeLinkSpider()
     home_link_spider.parse_page()
     home_link_spider.write_csv_file()
     end = time.time()
     print("耗时:{}秒".format(end-start))

注意:我们使用了 fake_useragent, requests和BeautifulSoup,这些都需要通过 pip 事先安装好才能用。

现在我们来看下爬取结果,耗时约 18.5 秒,总共爬取 580 条数据。

requests + parsel 组合

这次我们同样采用 requests 获取目标网页内容,使用 parsel 库(事先需通过 pip 安装)来解析。Parsel 库的用法和 BeautifulSoup 相似,都是先创建实例,然后使用各种选择器提取 DOM 元素和数据,但语法上稍有不同。Beautiful 有自己的语法规则,而 Parsel 库支持标准的 css 选择器和 xpath 选择器, 通过 get 方法或 getall 方法获取文本或属性值,使用起来更方便。

 # BeautifulSoup的用法
 from bs4 import BeautifulSoup


 soup = BeautifulSoup(response.text, 'html.parser')
 ul = soup.find_all("ul", class_="sellListContent")[0]


 # Parsel的用法, 使用Selector类
 from parsel import Selector
 selector = Selector(response.text)
 ul = selector.css('ul.sellListContent')[0]


 # Parsel获取文本值或属性值案例
 selector.css('div.title span::text').get()
 selector.css('ul li a::attr(href)').get()
 >>> for li in selector.css('ul > li'):
 ...     print(li.xpath('.//@href').get())

注:老版的 parsel 库使用extract()extract_first()方法获取文本或属性值,在新版中已被get()getall()方法替代。

全部代码如下所示:

 # homelink_parsel.py
 # Author: 大江狗
 from fake_useragent import UserAgent
 import requests
 import csv
 import re
 import time
 from parsel import Selector


 class HomeLinkSpider(object):
     def __init__(self):
         self.ua = UserAgent()
         self.headers = {"User-Agent": self.ua.random}
         self.data = list()
         self.path = "浦东_三房_500_800万.csv"
         self.url = "https://sh.lianjia.com/ershoufang/pudong/a3p5/"


     def get_max_page(self):
         response = requests.get(self.url, headers=self.headers)
         if response.status_code == 200:
             # 创建Selector类实例
             selector = Selector(response.text)
             # 采用css选择器获取最大页码div Boxl
             a = selector.css('div[class="page-box house-lst-page-box"]')
             # 使用eval将page-data的json字符串转化为字典格式
             max_page = eval(a[0].xpath('//@page-data').get())["totalPage"]
             print("最大页码数:{}".format(max_page))
             return max_page
         else:
             print("请求失败 status:{}".format(response.status_code))
             return None


     def parse_page(self):
         max_page = self.get_max_page()
         for i in range(1, max_page + 1):
             url = 'https://sh.lianjia.com/ershoufang/pudong/pg{}a3p5/'.format(i)
             response = requests.get(url, headers=self.headers)
             selector = Selector(response.text)
             ul = selector.css('ul.sellListContent')[0]
             li_list = ul.css('li')
             for li in li_list:
                 detail = dict()
                 detail['title'] = li.css('div.title a::text').get()


                 #  2室1厅 | 74.14平米 | 南 | 精装 | 高楼层(共6层) | 1999年建 | 板楼
                 house_info = li.css('div.houseInfo::text').get()
                 house_info_list = house_info.split(" | ")


                 detail['bedroom'] = house_info_list[0]
                 detail['area'] = house_info_list[1]
                 detail['direction'] = house_info_list[2]


                 floor_pattern = re.compile(r'\d{1,2}')
                 match1 = re.search(floor_pattern, house_info_list[4])  # 从字符串任意位置匹配
                 if match1:
                     detail['floor'] = match1.group()
                 else:
                     detail['floor'] = "未知"


                 # 匹配年份
                 year_pattern = re.compile(r'\d{4}')
                 match2 = re.search(year_pattern, house_info_list[5])
                 if match2:
                     detail['year'] = match2.group()
                 else:
                     detail['year'] = "未知"


                 # 文兰小区 - 塘桥    提取小区名和哈快
                 position_info = li.css('div.positionInfo a::text').getall()
                 detail['house'] = position_info[0]
                 detail['location'] = position_info[1]


                 # 650万,匹配650
                 price_pattern = re.compile(r'\d+')
                 total_price = li.css('div.totalPrice span::text').get()
                 detail['total_price'] = re.search(price_pattern, total_price).group()


                 # 单价64182元/平米, 匹配64182
                 unit_price = li.css('div.unitPrice span::text').get()
                 detail['unit_price'] = re.search(price_pattern, unit_price).group()
                 self.data.append(detail)


     def write_csv_file(self):


         head = ["标题", "小区", "房厅", "面积", "朝向", "楼层", 
                 "年份", "位置", "总价(万)", "单价(元/平方米)"]
         keys = ["title", "house", "bedroom", "area", 
                 "direction", "floor", "year", "location",
                 "total_price", "unit_price"]


         try:
             with open(self.path, 'w', newline='', encoding='utf_8_sig') as csv_file:
                 writer = csv.writer(csv_file, dialect='excel')
                 if head is not None:
                     writer.writerow(head)
                 for item in self.data:
                     row_data = []
                     for k in keys:
                         row_data.append(item[k])
                         # print(row_data)
                     writer.writerow(row_data)
                 print("Write a CSV file to path %s Successful." % self.path)
         except Exception as e:
             print("Fail to write CSV to path: %s, Case: %s" % (self.path, e))




 if __name__ == '__main__':
     start = time.time()
     home_link_spider = HomeLinkSpider()
     home_link_spider.parse_page()
     home_link_spider.write_csv_file()
     end = time.time()
     print("耗时:{}秒".format(end-start))

现在我们来看下爬取结果,爬取 580 条数据耗时约 16.5 秒,节省了 2 秒时间。可见 parsel 比 BeautifulSoup 解析效率是要高的,爬取任务少时差别不大,任务多的话差别可能会大些。

httpx 同步 + parsel 组合

我们现在来更进一步,使用 httpx 替代 requests 库。httpx 发送同步请求的方式和 requests 库基本一样,所以我们只需要修改上例中两行代码,把 requests 替换成 httpx 即可, 其余代码一模一样。

from fake_useragent import UserAgent
 import csv
 import re
 import time
 from parsel import Selector
 import httpx




 class HomeLinkSpider(object):
     def __init__(self):
         self.ua = UserAgent()
         self.headers = {"User-Agent": self.ua.random}
         self.data = list()
         self.path = "浦东_三房_500_800万.csv"
         self.url = "https://sh.lianjia.com/ershoufang/pudong/a3p5/"


     def get_max_page(self):


         # 修改这里把requests换成httpx
         response = httpx.get(self.url, headers=self.headers)
         if response.status_code == 200:
             # 创建Selector类实例
             selector = Selector(response.text)
             # 采用css选择器获取最大页码div Boxl
             a = selector.css('div[class="page-box house-lst-page-box"]')
             # 使用eval将page-data的json字符串转化为字典格式
             max_page = eval(a[0].xpath('//@page-data').get())["totalPage"]
             print("最大页码数:{}".format(max_page))
             return max_page
         else:
             print("请求失败 status:{}".format(response.status_code))
             return None


     def parse_page(self):
         max_page = self.get_max_page()
         for i in range(1, max_page + 1):
             url = 'https://sh.lianjia.com/ershoufang/pudong/pg{}a3p5/'.format(i)


              # 修改这里把requests换成httpx
             response = httpx.get(url, headers=self.headers)
             selector = Selector(response.text)
             ul = selector.css('ul.sellListContent')[0]
             li_list = ul.css('li')
             for li in li_list:
                 detail = dict()
                 detail['title'] = li.css('div.title a::text').get()


                 #  2室1厅 | 74.14平米 | 南 | 精装 | 高楼层(共6层) | 1999年建 | 板楼
                 house_info = li.css('div.houseInfo::text').get()
                 house_info_list = house_info.split(" | ")


                 detail['bedroom'] = house_info_list[0]
                 detail['area'] = house_info_list[1]
                 detail['direction'] = house_info_list[2]




                 floor_pattern = re.compile(r'\d{1,2}')
                 match1 = re.search(floor_pattern, house_info_list[4])  # 从字符串任意位置匹配
                 if match1:
                     detail['floor'] = match1.group()
                 else:
                     detail['floor'] = "未知"


                 # 匹配年份
                 year_pattern = re.compile(r'\d{4}')
                 match2 = re.search(year_pattern, house_info_list[5])
                 if match2:
                     detail['year'] = match2.group()
                 else:
                     detail['year'] = "未知"


                 # 文兰小区 - 塘桥    提取小区名和哈快
                 position_info = li.css('div.positionInfo a::text').getall()
                 detail['house'] = position_info[0]
                 detail['location'] = position_info[1]


                 # 650万,匹配650
                 price_pattern = re.compile(r'\d+')
                 total_price = li.css('div.totalPrice span::text').get()
                 detail['total_price'] = re.search(price_pattern, total_price).group()


                 # 单价64182元/平米, 匹配64182
                 unit_price = li.css('div.unitPrice span::text').get()
                 detail['unit_price'] = re.search(price_pattern, unit_price).group()
                 self.data.append(detail)


     def write_csv_file(self):


         head = ["标题", "小区", "房厅", "面积", "朝向", "楼层", 
                 "年份", "位置", "总价(万)", "单价(元/平方米)"]
         keys = ["title", "house", "bedroom", "area", "direction", 
                 "floor", "year", "location",
                 "total_price", "unit_price"]


         try:
             with open(self.path, 'w', newline='', encoding='utf_8_sig') as csv_file:
                 writer = csv.writer(csv_file, dialect='excel')
                 if head is not None:
                     writer.writerow(head)
                 for item in self.data:
                     row_data = []
                     for k in keys:
                         row_data.append(item[k])
                         # print(row_data)
                     writer.writerow(row_data)
                 print("Write a CSV file to path %s Successful." % self.path)
         except Exception as e:
             print("Fail to write CSV to path: %s, Case: %s" % (self.path, e))


 if __name__ == '__main__':
     start = time.time()
     home_link_spider = HomeLinkSpider()
     home_link_spider.parse_page()
     home_link_spider.write_csv_file()
     end = time.time()
     print("耗时:{}秒".format(end-start))


整个爬取过程耗时 16.1 秒,可见使用 httpx 发送同步请求时效率和 requests 基本无差别。

注意:Windows 上使用 pip 安装 httpx 可能会出现报错,要求安装 Visual Studio C++, 这个下载安装好就没事了。

接下来,我们就要开始王炸了,使用 httpx 和 asyncio 编写一个异步爬虫看看从链家网上爬取 580 条数据到底需要多长时间。

httpx 异步+ parsel 组合

Httpx 厉害的地方就是能发送异步请求。整个异步爬虫实现原理时,先发送同步请求获取最大页码,把每个单页的爬取和数据解析变为一个 asyncio 协程任务(使用async定义),最后使用 loop 执行。

大部分代码与同步爬虫相同,主要变动地方有两个:

     # 异步 - 使用协程函数解析单页面,需传入单页面url地址
     async def parse_single_page(self, url):


         # 使用httpx发送异步请求获取单页数据
         async with httpx.AsyncClient() as client:
             response = await client.get(url, headers=self.headers)
             selector = Selector(response.text)
             # 其余地方一样


     def parse_page(self):
         max_page = self.get_max_page()
         loop = asyncio.get_event_loop()


         # Python 3.6之前用ayncio.ensure_future或loop.create_task方法创建单个协程任务
         # Python 3.7以后可以用户asyncio.create_task方法创建单个协程任务
         tasks = []
         for i in range(1, max_page + 1):
             url = 'https://sh.lianjia.com/ershoufang/pudong/pg{}a3p5/'.format(i)
             tasks.append(self.parse_single_page(url))


         # 还可以使用asyncio.gather(*tasks)命令将多个协程任务加入到事件循环
         loop.run_until_complete(asyncio.wait(tasks))
         loop.close()


整个项目代码如下所示:

 from fake_useragent import UserAgent
 import csv
 import re
 import time
 from parsel import Selector
 import httpx
 import asyncio




 class HomeLinkSpider(object):
     def __init__(self):
         self.ua = UserAgent()
         self.headers = {"User-Agent": self.ua.random}
         self.data = list()
         self.path = "浦东_三房_500_800万.csv"
         self.url = "https://sh.lianjia.com/ershoufang/pudong/a3p5/"


     def get_max_page(self):
         response = httpx.get(self.url, headers=self.headers)
         if response.status_code == 200:
             # 创建Selector类实例
             selector = Selector(response.text)
             # 采用css选择器获取最大页码div Boxl
             a = selector.css('div[class="page-box house-lst-page-box"]')
             # 使用eval将page-data的json字符串转化为字典格式
             max_page = eval(a[0].xpath('//@page-data').get())["totalPage"]
             print("最大页码数:{}".format(max_page))
             return max_page
         else:
             print("请求失败 status:{}".format(response.status_code))
             return None


     # 异步 - 使用协程函数解析单页面,需传入单页面url地址
     async def parse_single_page(self, url):
         async with httpx.AsyncClient() as client:
             response = await client.get(url, headers=self.headers)
             selector = Selector(response.text)
             ul = selector.css('ul.sellListContent')[0]
             li_list = ul.css('li')
             for li in li_list:
                 detail = dict()
                 detail['title'] = li.css('div.title a::text').get()


                 #  2室1厅 | 74.14平米 | 南 | 精装 | 高楼层(共6层) | 1999年建 | 板楼
                 house_info = li.css('div.houseInfo::text').get()
                 house_info_list = house_info.split(" | ")


                 detail['bedroom'] = house_info_list[0]
                 detail['area'] = house_info_list[1]
                 detail['direction'] = house_info_list[2]




                 floor_pattern = re.compile(r'\d{1,2}')
                 match1 = re.search(floor_pattern, house_info_list[4])  # 从字符串任意位置匹配
                 if match1:
                     detail['floor'] = match1.group()
                 else:
                     detail['floor'] = "未知"


                 # 匹配年份
                 year_pattern = re.compile(r'\d{4}')
                 match2 = re.search(year_pattern, house_info_list[5])
                 if match2:
                     detail['year'] = match2.group()
                 else:
                     detail['year'] = "未知"


                  # 文兰小区 - 塘桥    提取小区名和哈快
                 position_info = li.css('div.positionInfo a::text').getall()
                 detail['house'] = position_info[0]
                 detail['location'] = position_info[1]


                  # 650万,匹配650
                 price_pattern = re.compile(r'\d+')
                 total_price = li.css('div.totalPrice span::text').get()
                 detail['total_price'] = re.search(price_pattern, total_price).group()


                 # 单价64182元/平米, 匹配64182
                 unit_price = li.css('div.unitPrice span::text').get()
                 detail['unit_price'] = re.search(price_pattern, unit_price).group()


                 self.data.append(detail)


     def parse_page(self):
         max_page = self.get_max_page()
         loop = asyncio.get_event_loop()


         # Python 3.6之前用ayncio.ensure_future或loop.create_task方法创建单个协程任务
         # Python 3.7以后可以用户asyncio.create_task方法创建单个协程任务
         tasks = []
         for i in range(1, max_page + 1):
             url = 'https://sh.lianjia.com/ershoufang/pudong/pg{}a3p5/'.format(i)
             tasks.append(self.parse_single_page(url))


         # 还可以使用asyncio.gather(*tasks)命令将多个协程任务加入到事件循环
         loop.run_until_complete(asyncio.wait(tasks))
         loop.close()




     def write_csv_file(self):
         head = ["标题", "小区", "房厅", "面积", "朝向", "楼层",
                 "年份", "位置", "总价(万)", "单价(元/平方米)"]
         keys = ["title", "house", "bedroom", "area", "direction",
                 "floor", "year", "location",
                 "total_price", "unit_price"]


         try:
             with open(self.path, 'w', newline='', encoding='utf_8_sig') as csv_file:
                 writer = csv.writer(csv_file, dialect='excel')
                 if head is not None:
                     writer.writerow(head)
                 for item in self.data:
                     row_data = []
                     for k in keys:
                         row_data.append(item[k])
                     writer.writerow(row_data)
                 print("Write a CSV file to path %s Successful." % self.path)
         except Exception as e:
             print("Fail to write CSV to path: %s, Case: %s" % (self.path, e))
 
 if __name__ == '__main__':
     start = time.time()
     home_link_spider = HomeLinkSpider()
     home_link_spider.parse_page()
     home_link_spider.write_csv_file()
     end = time.time()
     print("耗时:{}秒".format(end-start))

现在到了见证奇迹的时刻了。从链家网上爬取了 580 条数据,使用 httpx 编写的异步爬虫仅仅花了 2.5 秒!!

对比与总结

爬取同样的内容,采用不同工具组合耗时是不一样的。httpx 异步+parsel 组合毫无疑问是最大的赢家, requests 和 BeautifulSoup 确实可以功成身退啦。

  • requests + BeautifulSoup: 18.5 秒

  • requests + parsel: 16.5 秒

  • httpx 同步 + parsel: 16.1 秒

  • httpx 异步 + parsel: 2.5 秒

End

「进击的Coder」专属学习群已正式成立,搜索「CQCcqc4」添加崔庆才的个人微信或者扫描下方二维码拉您入群交流学习。

看完记得关注@进击的Coder

及时收看更多好文

↓↓↓

点个在看你最好看

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python 爬虫下一代网络请求库 httpx 和 parsel 解析库测评 的相关文章

  • 静态库、共享库、动态库概念?

    通常库分为 xff1a 静态库 共享库 xff0c 动态加载库 下面分别介绍 一 静态库 xff1a 1 概念 xff1a 静态库就是一些目标文件的集合 xff0c 以 a结尾 静态库在程序链接的时候使用 xff0c 链接器会将程序中使用
  • 链表——怎么写出正确的链表?

    链表 相比数组 xff0c 链表不需要一块连续的内存空间 xff0c 而是通过指针将一组零散的内存块串联起来使用 xff0c 而这里的内存块就叫做节点 xff0c 一般节点除了保存data还会保存下一个节点的地址也就是指针 单链表 头节点
  • 【STM32】STM32 变量存储在片内FLASH的指定位置

    在这里以STM32L4R5为例 xff08 官方出的DEMO板 xff09 xff0c 将变量存储在指定的片内FLASH地址 xff08 0x081F8000 xff09 一 MDK Keil软件操作 uint8 t version spa
  • 【STM32】 利用paho MQTT&WIFI 连接阿里云

    ST联合阿里云推出了云接入的相关培训 xff08 基于STM32的端到端物联网全栈开发 xff09 xff0c 所采用的的板卡为NUCLEO L4R5ZI板 xff0c 实现的主要功能为采集温湿度传感器上传到阿里云物联网平台 xff0c 并
  • 【STM32 】通过ST-LINK utility 实现片外FLASH的烧写

    目录 前言 一 例程参考及讲解 1 1 Loader Src c文件 1 2 Dev Inf c文件 二 程序修改 三 实测 参考 前言 在单片机的实际应用中 xff0c 通常会搭载一些片外FLASH芯片 xff0c 用于存储系统的一些配置
  • 基于FFmpeg的推流器(UDP推流)

    一 推流器对于输入输出文件无要求 1 输入文件可为网络流URL xff0c 可以实现转流器 2 将输入的文件改为回调函数 xff08 内存读取 xff09 的形式 xff0c 可以推送内存中的视频数据 3 将输入文件改为系统设备 xff08
  • 十进制转十六进制的C语言实现

    include lt stdio h gt include lt stdlib h gt include lt string h gt void reversestr char source char target unsigned int
  • Linux中断处理的“下半部”机制

    前言 中断分为硬件中断 xff0c 软件中断 中断的处理原则主要有两个 xff1a 一个是不能嵌套 xff0c 另外一个是越快越好 在Linux中 xff0c 分为中断处理采用 上半部 和 下半部 处理机制 一 中断处理 下半部 机制 中断
  • Linux中的workqueue机制

    转载与知乎https zhuanlan zhihu com p 91106844 一 前言 Linux中的workqueue机制是中断底半部的一种实现 xff0c 同时也是一种通用的任务异步处理的手段 进入workqueue队列处理的任务
  • 嵌入式编程通用Makefile

    一 根目录下Makefile 这个Makefile为主Makefile CROSS COMPILE span class token operator 61 span AS span class token operator 61 span
  • Hi3798 PWM输出控制背光

    一 PWM配置说明 Hi3798 具有3个PWM输出端口 通过查阅 Hi3798M V200 低功耗方案 使用指南 pdf 可得 xff1a 通过查阅Hitool工具可以查看到三个PWM端口的寄存器分别为 xff1a 通过原理图可得 xff
  • Hi3798移植4G模块(移远EC20)

    Hi3798移植4G模块 xff08 移远EC20 xff09 一 前言二 USB驱动修改2 1 添加VID和PID信息2 2 添加空包处理机制2 3 添加复位重连机制2 4 修改内核配置 三 GoBiNet测试程序 一 前言 本次系统采用
  • uniapp小视频项目:滑动播放视频

    文章目录 1 监听视频滑动2 播放和暂停3 增加播放 暂停视频功能4 增加双击点赞5 控制首个视频自动播6 动态渲染视频信息 1 监听视频滑动 给 swiper 增加 64 change 61 34 change 34 xff0c 这个时间
  • Vue github用户搜索案例

    文章目录 完成样式请求数据完善使用 vue resource 完成样式 1 public 下新建 css 文件夹 xff0c 放入下载好的 bootstrap css xff0c 并在 index html 中引入 2 新建 Search
  • 【敬伟ps教程】平移、缩放、移动、选区

    文章目录 平移抓手工具旋转抓手 缩放工具移动工具详解选区选区工具详解 平移 抓手工具 当打开一张大图时 xff0c 可以通过修改底部的百分比或使用抓手工具 xff08 H或在任何时候按住空格键来使用抓手工具 xff09 来查看更多细节 使用
  • 【敬伟ps教程】套索、魔棒工具、快速选择工具、选区的编辑和调整

    文章目录 套索工具自由套索多边形套索磁性套索工具 魔棒工具快速选择工具选区的编辑和调整 套索工具 自由套索 套索工具的用法 xff0c 点击鼠标左键拖动鼠标建立选区 当选区没闭合时 xff0c 松开鼠标会自动闭合选区 套索工具灵活快速但不够
  • Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

    项目是 vite 43 vue 43 ts 运行 npm run dev可以正常运行 xff0c 运行 npm run build 报错 解决办法 xff1a 1 先打开cmd全局命令窗口 xff0c 输入 npm install g in
  • 安卓手机投屏到win10电脑

    PC端操作 手机端操作 xff08 Mi6为例 xff09 pc端弹出提示 xff0c 选择是
  • 用Windows自带画图软件吸取色值

    1 打开画图windows自带画图软件 2 用qq截图要吸取颜色的图片 xff0c ctrl 43 v粘贴到画图软件中 3 点击取色器 xff0c 吸取颜色 xff0c 这是会看到吸取成功的颜色 4 打开编辑颜色 5 这样就得到了RGB颜色
  • 打开浏览器默认是360导航解决办法

    Chrome Chrome的设置中已经设置了百度为启动页 但是打开Chrome显示的还是360 解决办法很简单就是把桌面的快捷方式删除 xff0c 然后在安装目录重新生成快捷方式到桌面即可 Microsoft Edge 这个360修改的就更

随机推荐

  • 打开项目报错Error:Could not get unknown property 'mave' for project ':app' of type org.gradle.api.Project.

    今天打开项目 xff0c 报错如下 xff1a 打开gradle properties xff0c 发现最后的MAVEN URL地址错乱 xff0c 改完就可以了
  • Eclipse自带的抓包工具

    打开Eclipse window show view other 现在访问之前写的项目 http localhost 8888 android jsp flight index jsp 查看Eclipse
  • Fiddler抓取手机端APP接口数据(包括https)

    下载安装Fiddler https pan baidu com s 12zAt0r8lcHTszekOOcqeLg 环境要求 PC机和手机连接在同一网络下 设置 1 记录pc端地址 2 如果不显示这个工具栏 xff0c 可以设置View S
  • 【Git】Git撤销add操作

    Git add错了文件怎么办 xff1f 可以查看以下两篇 https git scm com book zh v1 Git 基础 记录每次更新到仓库 https git scm com book zh v1 Git 基础 撤消操作 我们来
  • Cygwin安装教程

    简介 cygwin是一个在windows平台上运行的unix模拟环境 xff0c 是cygnus solutions公司开发的自由软件 Cygwin就是一个windows软件 xff0c 该软件就是在windows上仿真linux操作系统
  • 学习Kalibr工具--Camera标定过程

    这里介绍用kalibr工具对相机进行单目和双目的标定 xff1b 在kalibr中不仅提供了IMU与camera的联合标定工具 xff0c 也包含了camera的标定工具箱 xff1b 准备 安装好kalibr之后 xff0c 开始准备标定
  • 常用NMEA0183的报文解析

    NMEA0183报文包括GPGGA GPRMC GPVTG等报文 xff0c 本文主要介绍NMEA0183语句报文的格式以及解析 xff0c 方便有关位置信息编程或者有关位置获取的其他方面 1 GPGGA GPGGA消息包含详细的GNSS定
  • 03 - 雷达的基本组成

    目录 1 雷达发射机 2 雷达天线 3 雷达接收机 4 雷达信号处理机 5 雷达终端设备 以典型单基地脉冲雷达为例来说明雷达的基本组成及其作用 如图1 5所示 xff0c 它主要由天线 发射机 接收机 信号处理机和终端设备等组成 1 雷达发
  • 相机模型详解

    相机模型 数码相机图像拍摄的过程实际上是一个光学成像的过程 相机的成像过程涉及到四个坐标系 xff1a 世界坐标系 相机坐标系 图像坐标系 像素坐标系 以及这四个坐标系的转换 理想透视模型 针孔成像模型 相机模型是光学成像模型的简化 xff
  • Socket通讯实验总结

    网络编程的第一个实验入门比较难 因为要理解透彻套接字的工作原理 xff0c 服务器与客户端通讯的过程 不过经过几天的仔细研究 xff0c 实验还是完成了 以下对几个实验的知识点总结一下 xff1a 1 Socket和线程 在实验中一定要弄清
  • Linux网络编程 - 基于UDP的服务器端/客户端

    一 理解UDP 1 0 UDP协议简介 UDP User Datagram Protocol xff0c 用户数据报协议 RFC 768 UDP协议的数据传输单元叫 UDP用户数据报 xff0c 而TCP协议的数据传输单元叫 TCP报文段
  • VS中MFC连接MySQL的方法

    MFC 连接 MySQL 的方法 xff1a 首先建立一个 MFC 项目 下面进行设置 xff1a xff08 1 xff09 项目 gt 属性 gt 配置属性 gt C C 43 43 gt 附加包含目录 xff1a 在附加包含目录中添加
  • MFC 窗口Dialog 添加背景图片

    xff08 1 xff09 添加要设置为背景的图片资源 xff08 格式为bmp xff09 将图片 xff08 命名为homepage bmp xff09 放到工程下的资源文件夹中 xff08 res xff09 xff08 2 xff0
  • VirtualBox 在CentOS下安装增强功能及错误解决

    安装步骤如下 xff1a 1 执行安装增强功能 xff1a 之后会出现 Building the main Guest Addtional module Failed 的错误 xff0c 如下图 xff0c 安装失败 xff01 xff01
  • sem_open、sem_close、sem_unlink

    UNP2 P180 sem t sem open const char name int oflag mode t mode unsigned int value 打开有名信号量 1 当打开一个一定存在的有名信号量时 xff0c ofalg
  • GetLastError() 显示错误信息

    LPVOID lpMsgBuf FormatMessage FORMAT MESSAGE ALLOCATE BUFFER FORMAT MESSAGE FROM SYSTEM FORMAT MESSAGE IGNORE INSERTS NU
  • 线程SuspendThread() ResumeThread()的使用

    SuspendThread xff1a 挂起线程 If the function succeeds the return value is the thread 39 s previous suspend count otherwise i
  • DOS 命令访问FTP错误:425Failed to establish connection

    在windows命令窗口访问FTP服务会出现425Failed to establish connection的错误 解决办法 xff1a 1 关闭本队防火墙 2 在防火墙允许通过的程序中 xff0c 根据所用网络勾选 文件传送程序
  • 串口通信协议简介—学习笔记

    串口通信协议简介 学习笔记 文章目录 串口通信协议简介 学习笔记一 串口 COM口 UART口 TTL RS 232 RS 485区别详解1 物理接口形式2 电平标准2 1 TTL 2 2 RS232 2 3 RS485 2 4 TTL标准
  • Python 爬虫下一代网络请求库 httpx 和 parsel 解析库测评

    这是 进击的Coder 的第 437 篇技术分享 作者 xff1a 大江狗 来源 xff1a Python Web与Django开发 阅读本文大概需要 8 分钟 Python 网络爬虫领域两个最新的比较火的工具莫过于 httpx 和 par