Day6 lxml和多线程
文章目录
1. xpath语法
1.1 xpath基本概念
引入模块
from lxml import etree
1)树:
2)节点:树结构中的每一个元素(标签)就是一个节点
3)根节点(根元素):html或者xml最外面的那个标签(元素)
4)节点内容:标签内容
5)节点属性:标签属性
1.2 xml数据格式
xml和json一样,是一种通过的数据格式(绝大部分编程语言都支持的数据格式)
xml是通过标签(元素)的标签内容和标签属性来保存数据的。
注:先保存一个文件名为data.html的超市信息(模拟网页))
<supermarket name="永辉超市" address="肖家河大厦">
<staffs>
<staff id="s001">
<name>小明</name>
<position>收营员</position>
<salary>4000</salary>
</staff>
<staff id="s002">
<name>小花</name>
<position>促销员</position>
<salary>3500</salary>
</staff>
<staff id="s003">
<name>张三</name>
<position>保洁</position>
<salary>3000</salary>
</staff>
<staff id="s004">
<name>李四</name>
<position>收营员</position>
<salary>4000</salary>
</staff>
<staff id="s005">
<name>王五</name>
<position>售货员</position>
<salary>3800</salary>
</staff>
</staffs>
<goodsList>
<goods discount="0.9">
<name>泡面</name>
<price>3.5</price>
<count>120</count>
</goods>
<goods>
<name>火腿肠</name>
<price>1.5</price>
<count>332</count>
</goods>
<goods>
<name>矿泉水</name>
<price>2</price>
<count>549</count>
</goods>
<goods discount="8.5">
<name>面包</name>
<price>5.5</price>
<count>29</count>
</goods>
</goodsList>
</supermarket>
1.3 xpath语法
1.3.1 创键树结构获取树的根节点
# etree.XML(xml数据)
# etree.HTML(html数据)
f = open('files/data.xml', encoding='utf-8')
root = etree.XML(f.read())
f.close()
1.3.2 根据xpath获取指定标签
节点对象.xpath(路径) - 返回路径对应的所有的标签,返回值是列表,列表中的元素是标签对象
路径的写法:
a. 绝对路径:用”/“开头的路径 - /标签在树结构中的全路径 (路径必须从根节点开始写)
b. 相对路径:用“.“开头表示当前节点(xpath前面是谁,“.”代表的就是谁,”…“表示当前节点的上层节点
c. 全路径 :用“//”开头的路径 - 在整个数中获取标签
注意:绝对路径和全路径的写法和查找方式和是用谁去点的xpath无关
result = root.xpath('/supermarket/staffs/staff/name/text()')
print(result) # ['小明', '小花', '张三', '李四', '王五']
result = root.xpath('./staffs/staff/name/text()')
print(result) # ['小明', '小花', '张三', '李四', '王五']
# 获取第一个员工对应的staff标签
staff1 = root.xpath('./staffs/staff')[0] # 获取第一个员工对应的staff标签
result = staff1.xpath('./name/text()')
print(result) # ['小明']
result = staff1.xpath('../staff/name/text()')
print(result) # ['小明', '小花', '张三', '李四', '王五']
result = root.xpath('//name/text()')
print(result) # ['小明', '小花', '张三', '李四', '王五', '泡面', '火腿肠', '矿泉水', '面包']
result = staff1.xpath('//goods/name/text()')
print(result) # ['泡面', '火腿肠', '矿泉水', '面包']
1.3.3获取标签内容
节点对象.xpath(获取标签的路径/text()) - 获取指定路径下所有标签的标签内容
result = root.xpath('//position/text()')
print(result) # ['收营员', '促销员', '保洁', '收营员', '售货员']
1.3.4 获取标签属性值
节点对象.xpath(获取标签的路径/@属性名)
result = root.xpath('/supermarket/@name')
print(result) # ['永辉超市']
result = root.xpath('//staff/@id')
print(result) # ['s001', 's002', 's003', 's004', 's005']
1.3.5 谓语(条件)
a. 位置相关谓语
- [N] - 第N个
- [last()] - 最后一个
- [last()-N]; [last()-1] - 倒数第2个 、 [last()-2] - 倒数第3个
- [position()>N]、[position()<N]、[position()>=N]、[position()<=N] 前几个后几个
result = root.xpath('//staff[1]/name/text()')
print(result) # ['小明']
result = root.xpath('//staff[last()]/name/text()')
print(result) # ['王五']
result = root.xpath('//staff[last()-1]/name/text()')
print(result) # ['李四']
result = root.xpath('//staff[position()<3]/name/text()')
print(result) # ['小明', '小花']
b.属性相关谓语
- [@属性名=属性值] - 指定属性是指定值的标签
- [@属性名] - 拥有指定属性的标签
# staff[@class="c1"] == staff.c1
result = root.xpath('//staff[@class="c1"]/name/text()')
print(result) # [] (没有class值为c1的,所有为空)
result = root.xpath('//staff[@id="s003"]/name/text()')
print(result) # ['张三']
result = root.xpath('//goods[@discount]/name/text()')
print(result) # ['泡面', '面包']
c.子标签内容相关谓语
根据子标签的内容来筛选标签
- [子标签名>数据]
- [子标签名<数据]
- [子标签名>=数据]
- [子标签名<=数据]
- [子标签名=数据]
result = root.xpath('//goods[price=2]/name/text()')
print(result) # ['矿泉水']
1.3.6 通配符
写路径的时候用*来表示所有标签或者所有属性
result = root.xpath('//staff[1]/*/text()')
print(result) # ['小明', '收营员', '4000']
# *[@class="c1"] == .c1
result = root.xpath('//*[@class="c1"]/name/text()')
print(result) # []
result = root.xpath('//goods[@*]/name/text()')
print(result) # ['泡面', '面包']
result = root.xpath('/supermarket/@*')
print(result) # ['永辉超市', '肖家河大厦']
1.3.7 若干路径 —— |
路径1|路径2 —— 同时获取路径1和路径2的内容
result = root.xpath('//goods/name/text()|//staff/position/text()')
print(result) # ['收营员', '促销员', '保洁', '收营员', '售货员', '泡面', '火腿肠', '矿泉水', '面包']
2. 解析某瓣电影
'''
Author:KathAmy
Date:2022/8/18 11:45
键盘敲烂,共同进步!
'''
import requests
import csv
from lxml import etree
def get_net_data():
url = 'https://movie.douban.com/top250'
Headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.81 Safari/537.36 Edg/104.0.1293.47'
}
response = requests.get(url, headers=Headers)
return response.text
def analysis_data(html: str):
root = etree.HTML(html)
names = root.xpath('//ol[@class="grid_view"]/li/div/div[@class="info"]/div[1]/a/span[1]/text()')
# print(names) 测试一下
scores = root.xpath('//ol[@class="grid_view"]/li/div/div[@class="info"]/div[2]/div/span[2]/text()')
# print(scores)
comment_count = root.xpath('//ol[@class="grid_view"]/li/div/div[@class="info"]/div[2]/div/span[last()]/text()')
# print(comment_count)
slogan = root.xpath('//ol[@class="grid_view"]/li/div/div[@class="info"]/div[2]/p[@class="quote"]/span/text()')
# print(slogan)
all_data = list(map(lambda i1, i2, i3, i4: [i1, i2, i3, i4], names, scores, comment_count, slogan))
with open('电影.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerow(['电影名', '评分', '评论数', '标语'])
writer.writerows(all_data)
def analysis_data2(html: str):
root = etree.HTML(html)
all_film_div = root.xpath('//ol[@class="grid_view"]/li/div')
all_data = []
for div in all_film_div:
names = div.xpath('./div[@class="info"]/div[1]/a/span[1]/text()')[0]
scores = div.xpath('./div[@class="info"]/div[2]/div/span[2]/text()')[0]
comment_count = div.xpath('./div[@class="info"]/div[2]/div/span[last()]/text()')[0]
slogan = div.xpath('./div[@class="info"]/div[2]/p[@class="quote"]/span/text()')[0]
all_data.append([names, scores, comment_count, slogan])
# print(all_data)
# print('-----------------------------------华丽的分割线-----------------------------------')
with open('电影2.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerow(['电影名', '评分', '评论数', '标语'])
writer.writerows(all_data)
if __name__ == '__main__':
data = get_net_data()
analysis_data2(data)
3. 多线程
3.1 基本理论
3.1.1 进程和线程
进程: 一个正在运行的应用程序就是一个进程,每个进程均运行在其专门且受保护的内存空间中
线程: 线程是进程执行人物的基本单元(一个进程中的人物都是在线程中执行的)
进程就是车间,线程就是车间里面的工人。
一个进程中默认有一个线程,这个线程叫主线程。
3.1.2 线程的特点
如果在一个线程中执行多个任务,任务是串行执行的。
(当一个程序中有很多个任务的时候,如果只有一个线程,那么程序执行的效率比较低)
3.1.3 多线程
一个进程中有多个线程就是多线程。
多线程执行任务的时候,多个任务可以同时(并行)/(一起)执行。
3.1.4 多线程原理
一个cpu同一时间只能调度一个线程,多线程其实是cpu快速的在多个线程之间进行切换,造成多个线程同时执行的假象。
(提高cpu的利用率)
3.2 Python使用多线程的方法
一个进程默认只有一个线程,这个线程叫主线程,主线程以外的线程都叫子线程。
Python程序中如果需要子线程,必须创建线程类(Thread)的对象。
from threading import Thread
from time import sleep
from datetime import datetime
def download(name):
print(f'{name}开始下载:{datetime.now()}')
sleep(2)
print(f'{name}下载结束:{datetime.now()}')
if __name__ == '__main__':
# 情况1:在一个线程(主线程)中下载3个电影
download('肖生克的救赎')
download('霸王别姬')
download('阿甘正传')
# 情况2:使用3个子线程分别下载3个电影
# 1)创建线程对象
'''
线程对象 = Thread(target=函数, args=元组) target - 目标 args - 参数
a.函数 - 可以是普通函数函数名,也可以是匿名函数。这个函数就是需要在子线程中执行的任务。
b.元组 - 元组中的元素就是在子线程中调用target对应的函数的时候对应的参数
'''
t1 = Thread(target=download, args=('肖生克的救赎',))
t2 = Thread(target=download, args=('霸王别姬',))
t3 = Thread(target=download, args=('阿甘正传',))
# 2)启动线程 - 让子线程调用对应的函数
t1.start()
t2.start()
t3.start()
4. 多线程解析某瓣电影
'''
Author:KathAmy
Date:2022/8/18 19:22
键盘敲烂,共同进步!
'''
import requests
from datetime import datetime
from threading import Thread
import csv
from lxml import etree
from bs4 import BeautifulSoup
Headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.81 Safari/537.36 Edg/104.0.1293.47'
}
def get_one_page_data(page):
print(f'第{page}页开始获取:{datetime.now()}')
url = fr'https://movie.douban.com/top250?start={page}&filter='
response = requests.get(url, headers=Headers)
print('下载完成!')
print(f'第{page}页开始获取:{datetime.now()}')
def analysis_data(html: str):
soup = BeautifulSoup(html, 'lxml')
all_film_div = soup.select('.grid_view>li>.item') # 返回的是一个列表
all_film = []
for div in all_film_div:
name = div.select_one('.title').text
score = div.select_one('.rating_num').text
info = div.select_one('.bd>p').text.strip().split('\n')[-1].strip()
# time, country, category = info.split('/')
info_list = info.split('/')
time = info_list[0] # 避免格式不统一,集体切割就OK
country = info_list[-2] # 避免格式不统一,集体切割就OK
category = info_list[-1] # 避免格式不统一,集体切割就OK
slogan = div.select_one('.inq')
if slogan:
slogan = slogan.text
else:
slogan = ''
comment = div.select('.star>span')[-1].text[:-3]
print(name)
print(score)
print(time, country, category)
print(slogan)
print(comment)
print('-----------------------------------华丽的分割线-----------------------------------')
# all_film.append([name, score, time.strip(), country.strip(), category.strip(), slogan, comment])
# with open('电影3.csv', 'w', encoding='utf-8', newline='') as f:
# writer = csv.writer(f)
# writer.writerow(['电影名', '评分', '上映时间', '国家', '类型', '标语', '评论人数'])
# writer.writerows(all_film)
if __name__ == '__main__':
for page in range(0, 256, 25):
t = Thread(target=get_one_page_data, args=(page,))
t.start()