ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

数据提取

2019-11-22 11:58:39  阅读:213  来源: 互联网

标签:提取 author url self soup html print 数据


非结构化数据处理(文本)
正则
match 方法:从起始位置开始查找,一次匹配
# match 方法用于查找字符串的头部(也可以指定起始位置),它是一次匹配,只要找到了一个匹配的结果就返回,而不是查找所有匹配的结果。它的一般使用形式如下:
# 其中,string 是待匹配的字符串,pos 和 endpos 是可选参数,指定字符串的起始和终点位置,默认值分别是 0 和 len (字符串长度)。
# 不指定 pos 和 endpos 时,match 方法默认匹配字符串的头部。
#当匹配成功时,返回一个 Match 对象,如果没有匹配上,则返回 None。
import re
pattern = re.compile(r'\d+') # 用于匹配至少一个数字
m = pattern.match('one12twothree34four') # 查找头部,没有匹配
print (m)
m = pattern.match('one12twothree34four', 2, 10) # 从'e'的位置开始匹配,没有匹配
m = pattern.match('one12twothree34four', 3, 10) # 从'1'的位置开始匹配,正好匹配

group()分组匹配, 提取数据
pattern = re.compile(r'([a-z]+) ([a-z]+)', re.I) # re.I 表示忽略大小写
m = pattern.match('Hello World Wide Web')
print (m) # 匹配成功,返回一个 Match 对象
#<_sre.SRE_Match object at 0x10bea83e8>

m.group(0) # 返回匹配成功的整个子串
#'Hello World'
m.group(1) # 返回第一个分组匹配成功的子串
#'Hello'
m.group(2) # 返回第二个分组匹配成功的子串
#'World'
m.span(0) # 返回匹配成功的整个子串的索引
#(0, 11)
m.span(1) # 返回第一个分组匹配成功的子串的索引
#(0, 5)


search 方法:从任何位置开始查找,一次匹配
# search 方法用于查找字符串的任何位置,它也是一次匹配,只要找到了一个匹配的结果就返回,而不是查找所有匹配的结果,它的一般使用形式如下:
# 当匹配成功时,返回一个 Match 对象,如果没有匹配上,则返回 None。
pattern = re.compile('\d+')
m = pattern.search('one12twothree34four') # 这里如果使用 match 方法则不匹配
# <_sre.SRE_Match object at 0x10cc03ac0>
m.group()
# '12'

findall 方法:全部匹配,返回列表
# 搜索整个字符串,获得所有匹配的结果。
pattern = re.compile(r'\d+') # 查找数字
result1 = pattern.findall('hello 123456 789')
result2 = pattern.findall('one1two2three3four4', 0, 10)
print (result1)
print (result2)
# ['123456', '789']
# ['1', '2']

finditer 方法:全部匹配,返回迭代器

split 方法:分割字符串,返回列表

p = re.compile(r'[\s\,\;]+')
print (p.split('a,b;; c d'))
# ['a', 'b', 'c', 'd']

sub 方法:替换
p = 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'

xpath
表达式
nodename 选取此节点的所有子节点。
/ 从根节点选取。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。

路径
bookstore 选取 bookstore 元素的所有子节点。
/bookstore 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。
//book 选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang 选取名为 lang 的所有属性。

谓语
/bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。
/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()<3] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang=’eng’] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

多个路径
//book/title | //book/price 选取 book 元素的所有 title 和 price 元素。
//title | //price 选取文档中的所有 title 和 price 元素。
/bookstore/book/title | //price 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。

lxml库
使用xpath之前需要先将响应信息转换为xml结构
安装: pip install lxml
# 1. 从字符串中解析
# 使用 lxml 的 etree 库
from lxml import etree

text = '''
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a> # 注意,此处缺少一个 </li> 闭合标签
</ul>
</div>
'''
#利用etree.HTML,将字符串解析为HTML文档
# lxml可以修正缺少的html标签
html = etree.HTML(text)
# 按字符串序列化HTML文档
result = etree.tostring(html)
print(result)

# 2. 从文件中解析
from lxml import etree

# 读取外部文件 hello.html
html = etree.parse('./hello.html')
result = etree.tostring(html, pretty_print=True)
# 谓语 [last()] 可以找到最后一个元素
result = html.xpath('//li[last()]/a/@href') # 获取href属性的值
result = html.xpath('//li[last()-1]/a').text # 获取a标签的文本
result = html.xpath('//*[@class="bold"]').tag # 获取标签名
print(result)


CSS (beautifulsoup4)
lxml 只会局部遍历,而Beautiful Soup 是基于HTML DOM的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml。
安装: pip install beautifulsoup4
基本使用
from bs4 import BeautifulSoup

html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
#创建 Beautiful Soup 对象
# soup = BeautifulSoup(html)
soup = BeautifulSoup(html,'lxml') # 指定lxml解析器
# 打开本地 HTML 文件的方式来创建对象
# soup = BeautifulSoup(open('index.html'))
# 格式化输出 soup 对象的内容
print soup.prettify()

bs4 的4大对象种类
Tag 标签
# 获取各类标签对象
print soup.title
# <title>The Dormouse's story</title>
print soup.head
# <head><title>The Dormouse's story</title></head>
print soup.a
# <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
print soup.p
# <p class="title" name="dromouse"><b>The Dormouse's story</b></p>
print type(soup.p)
# <class 'bs4.element.Tag'>

# 对于 Tag,它有两个重要的属性,是 name 和 attrs
print soup.head.name # 标签的名字
# head
print soup.p.attrs # 标签的所有属性
# {'class': ['title'], 'name': 'dromouse'}

print soup.p['class'] # 获取标签的某一个属性的值
# soup.p.get('class') # 等价于上面
# ['title'] #还可以利用get方法,传入属性的名称,二者是等价的

BeautifulSoup 整体页面DOM对象, 它是一个特殊的Tag对象
print type(soup.name)
# <type 'unicode'>
print soup.name
# [document]
print soup.attrs # 文档本身的属性为空
# {}

NavigableString 标签的内容
print soup.p.string
# The Dormouse's story
print type(soup.p.string)
# In [13]: <class 'bs4.element.NavigableString'>

Comment 注释对象 它是一个特殊的NavigableString
print soup.a
# <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
print soup.a.string
# Elsie
print type(soup.a.string)
# <class 'bs4.element.Comment'>

bs4 搜索
直接子节点 :.contents .children
# .content 属性可以将tag的子节点以列表的方式输出
print soup.head.contents
# [<title>The Dormouse's story</title>]
print soup.head.contents[0]
# <title>The Dormouse's story</title>
print soup.head.children
#<listiterator object at 0x7f71457f5710>
for child in soup.body.children:
print child

find_all(name, attrs, recursive, text, **kwargs) 返回list
# 1. 通过标签名查找
soup.find_all('b') # 查找所有b标签, 返回列表
# [<b>The Dormouse's story</b>]

# 2. 通过属性查找
soup.find_all(id='link2')
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

select() 返回list
# 1. 通过标签名查找
print soup.select('title')
#[<title>The Dormouse's story</title>]

# 2. 通过类名查找
print soup.select('.sister')
# [<a class="sister" href="http://example.com/ </a>]

# 3. 通过id查找
print soup.select('#link1')
#[<a class="sister" href="http://example.com/elsie" id="link1">链接</a>]

# 4. 通过属性查找
print soup.select('a[class="sister"]')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- --></a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
print soup.select('a[href="http://example.com/elsie"]')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- --></a>]

# 5. 组合查找
print soup.select('p #link1')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- --></a>]

# 6. 获取内容
soup = BeautifulSoup(html, 'lxml')
print type(soup.select('title'))
print soup.select('title')[0].get_text()

for title in soup.select('title'):
print title.get_text()

结构化数据处理(json)
使用json模块进行json字符串与python对象之间相互转换的操作
json.loads() json 转 python
import json

strList = '[1, 2, 3, 4]' # json的数组
strDict = '{"city": "北京", "name": "大猫"}' # json的对象
json.loads(strList) # json字符串转python列表
# [1, 2, 3, 4]
json.loads(strDict) # json数据自动按Unicode存储
# {u'city': u'\u5317\u4eac', u'name': u'\u5927\u732b'}

json.dumps() python 转 json
import json
import chardet # chardet是一个非常优秀的编码识别模块,可通过pip安装

listStr = [1, 2, 3, 4]
tupleStr = (1, 2, 3, 4)
dictStr = {"city": "北京", "name": "大猫"}
json.dumps(listStr)
# '[1, 2, 3, 4]'
json.dumps(tupleStr)
# '[1, 2, 3, 4]'

# 注意:json.dumps() 序列化时默认使用的ascii编码
# 添加参数 ensure_ascii=False 禁用ascii编码,按utf-8编码
# chardet.detect()返回字典, 其中confidence是检测精确度
json.dumps(dictStr)
# '{"city": "\\u5317\\u4eac", "name": "\\u5927\\u5218"}'
chardet.detect(json.dumps(dictStr)) # 检测编码
# {'confidence': 1.0, 'encoding': 'ascii'}
print json.dumps(dictStr, ensure_ascii=False)
# {"city": "北京", "name": "大刘"}
chardet.detect(json.dumps(dictStr, ensure_ascii=False))
# {'confidence': 0.99, 'encoding': 'utf-8'}

json.load() 从文件中直接转python
读取文件中json形式的字符串元素 转化成python类型
strList = json.load(open("listStr.json"))
# [{u'city': u'\u5317\u4eac'}, {u'name': u'\u5927\u5218'}]
strDict = json.load(open("dictStr.json"))
# {u'city': u'\u5317\u4eac', u'name': u'\u5927\u5218'}

json.dump() python转json写入文件
将Python内置类型序列化为json对象后写入文件
listStr = [{"city": "北京"}, {"name": "大刘"}]
json.dump(listStr, open("listStr.json","w"), ensure_ascii=False)

dictStr = {"city": "北京", "name": "大刘"}
json.dump(dictStr, open("dictStr.json","w"), ensure_ascii=False)

xpath抓取案例
糗事百科
#coding=utf-8
import requests
from retrying import retry
from lxml import etree

class Qiubai_spider():
def __init__(self):
self.url = "http://www.qiushibaike.com/8hr/page/{}/" # 链接模型
self.headers = {
"User-Agent":"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1 Trident/5.0;"
}

@retry(stop_max_attempt_number=5) #调用retry,当assert出错时候,重复请求5次
def parse_url(self,url):
response = requests.get(url,timeout=10,headers=self.headers) #请求url
assert response.status_code==200 #当响应码不是200时候,做断言报错处理
print(url)
return etree.HTML(response.text) #返回etree之后的html

def parse_content(self,html):
item_temp = html.xpath("//div[@class='article block untagged mb15']")
print(len(item_temp))
for item in item_temp:
#获取用户头像地址
avatar = item.xpath("./div[1]/a[1]/img/@src")[0] if len(item.xpath("./div[1]/a[1]/img/@src"))>0 else None
#为头像地址添加前缀
if avatar is not None and not avatar.startswith("http:"):
avatar = "http:"+avatar
name = item.xpath("./div[1]/a[2]/h2/text()")[0] #获取用户名
content = item.xpath("./a[@class='contentHerf']/div/span/text()")[0] #获取内容
star_number = item.xpath("./div[@class='stats']/span[1]/i/text()")[0] #获取点赞数
comment_number = item.xpath("./div[@class='stats']/span[2]/a/i/text()")[0] #获取评论数
print("*"*100)

def run(self):
'''函数的主要逻辑实现
'''
url = self.url.format(1) #获取到url
html = self.parse_url(url) #请求url
self.parse_content(html) #解析页面内容并把内容存入内容队列

if __name__ == "__main__":
qiubai = Qiubai_spider()
qiubai.run()


天气网
#encoding: utf-8

import requests
from bs4 import BeautifulSoup
from pyecharts import Bar

ALL_DATA = []

def parse_page(url):
headers = {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"
}
response = requests.get(url,headers=headers)
text = response.content.decode('utf-8')
# html5lib
# pip install html5lib
soup = BeautifulSoup(text,'html5lib')
conMidtab = soup.find('div',class_='conMidtab')
tables = conMidtab.find_all('table')
for table in tables:
trs = table.find_all('tr')[2:]
for index,tr in enumerate(trs):
tds = tr.find_all('td')
city_td = tds[0]
if index == 0:
city_td = tds[1]
city = list(city_td.stripped_strings)[0]
temp_td = tds[-2]
min_temp = list(temp_td.stripped_strings)[0]
ALL_DATA.append({"city":city,"min_temp":int(min_temp)})
# print({"city":city,"min_temp":int(min_temp)})

def main():
urls = [
'http://www.weather.com.cn/textFC/hb.shtml',
'http://www.weather.com.cn/textFC/db.shtml',
'http://www.weather.com.cn/textFC/hd.shtml',
'http://www.weather.com.cn/textFC/hz.shtml',
'http://www.weather.com.cn/textFC/hn.shtml',
'http://www.weather.com.cn/textFC/xb.shtml',
'http://www.weather.com.cn/textFC/xn.shtml',
'http://www.weather.com.cn/textFC/gat.shtml'
]
for url in urls:
parse_page(url)

# 分析数据
# 根据最低气温进行排序
ALL_DATA.sort(key=lambda data:data['min_temp'])

data = ALL_DATA[0:10]
cities = list(map(lambda x:x['city'],data))
temps = list(map(lambda x:x['min_temp'],data))
# pyecharts
# pip install pyecharts
chart = Bar("中国天气最低气温排行榜")
chart.add('',cities,temps)
chart.render('temperature.html')


if __name__ == '__main__':
main()
# ALL_DATA = [
# {"city": "北京", 'min_temp': 0},
# {"city": "天津", 'min_temp': -8},
# {"city": "石家庄", 'min_temp': -10}
# ]
#
# ALL_DATA.sort(key=lambda data:data['min_temp'])

多线程
Queue 常用方法
Queue是python中的标准库,可以直接import Queue引用;队列是线程间最常用的交换数据的形式
Queue是线程安全的
Queue.qsize() # 返回队列的大小
Queue.empty() # 如果队列为空,返回True,反之False
Queue.full() # 如果队列满了,返回True,反之False
Queue.full # 与 maxsize 大小对应
Queue.get([block[, timeout]]) #获取队列,timeout等待时间

创建队列
import Queue
myqueue = Queue.Queue(maxsize = 10)

插入队列
myqueue.put(10)

将一个值从队列中取出
myqueue.get()

多线程抓取案例
# coding=utf-8
import requests
from lxml import etree
import json
from queue import Queue
import threading

class Qiubai:
def __init__(self):
self.headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWeb\
Kit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"}
self.url_queue = Queue() # url队列
self.html_queue =Queue() # html响应信息队列
self.content_queue = Queue() # 内容队列


def get_total_url(self):
'''
获取了所有的页面url,并且返回urllist
return :list
'''
url_temp = 'https://www.qiushibaike.com/8hr/page/{}/' # url模板
for i in range(1,36):
self.url_queue.put(url_temp.format(i)) # url入队

def parse_url(self):
'''
一个发送请求,获取响应,同时etree处理html
'''
while self.url_queue.not_empty: # 判断url队列不为空
url = self.url_queue.get() # 从队列中取出一个url
print("parsing url:", url)
response = requests.get(url,headers=self.headers,timeout=10) # 发送请求
html = response.content.decode() #获取html字符串
html = etree.HTML(html) #获取element 类型的html
self.html_queue.put(html) # html响应入队
self.url_queue.task_done() # task_done的时候,队列计数减一

def get_content(self):
'''
:param url:
:return: 一个list,包含一个url对应页面的所有段子的所有内容的列表
'''
while self.html_queue.not_empty: # 判断html响应队列不为空
html = self.html_queue.get() # 从队列中取出一份html响应信息
# 解析内容
total_div = html.xpath('//div[@class="article block untagged mb15"]') #返回divelememt的一个列表
itme_list = [] # 用于保存最终所有的信息
for i in total_div: #遍历div标枪,获取糗事百科每条的内容的全部信息
author_img = i.xpath('./div[@class="author clearfix"]/a[1]/img/@src')
author_img = "https:" + author_img[0] if len(author_img) > 0 else None
author_name = i.xpath('./div[@class="author clearfix"]/a[2]/h2/text()')
author_name = author_name[0] if len(author_name) > 0 else None
author_href = i.xpath('./div[@class="author clearfix"]/a[1]/@href')
author_href = "https://www.qiushibaike.com" + author_href[0] if len(author_href) > 0 else None
author_gender = i.xpath('./div[@class="author clearfix"]//div/@class')
author_gender = author_gender[0].split(" ")[-1].replace("Icon", "") if len(author_gender) > 0 else None
author_age = i.xpath('./div[@class="author clearfix"]//div/text()')
author_age = author_age[0] if len(author_age) > 0 else None
content = i.xpath('./a[@class="contentHerf"]/div/span/text()')
content_vote = i.xpath('./div[@class="stats"]/span[1]/i/text()')
content_vote = content_vote[0] if len(content_vote) > 0 else None
content_comment_numbers = i.xpath('./div[@class="stats"]/span[2]/a/i/text()')
content_comment_numbers = content_comment_numbers[0] if len(content_comment_numbers) > 0 else None
hot_comment_author = i.xpath('./a[@class="indexGodCmt"]/div/span[last()]/text()')
hot_comment_author = hot_comment_author[0] if len(hot_comment_author) > 0 else None
hot_comment = i.xpath('./a[@class="indexGodCmt"]/div/div/text()')
hot_comment = hot_comment[0].replace("\n:", "").replace("\n", "") if len(hot_comment) > 0 else None
hot_comment_like_num = i.xpath('./a[@class="indexGodCmt"]/div/div/div/text()')
hot_comment_like_num = hot_comment_like_num[-1].replace("\n", "") if len(hot_comment_like_num) > 0 else None
item = dict(
author_name=author_name,
author_img=author_img,
author_href=author_href,
author_gender=author_gender,
author_age=author_age,
content=content,
content_vote=content_vote,
content_comment_numbers=content_comment_numbers,
hot_comment=hot_comment,
hot_comment_author=hot_comment_author,
hot_comment_like_num=hot_comment_like_num
)
item_list.append(item)
self.content_queue.put(items) # 内容入队
self.html_queue.task_done() # task_done的时候,队列计数-1

def save_items(self):
'''
保存items
:param items:列表
'''
while self.content_queue.not_empty: # 判断内容队列不为空
items = self.content_queue.get() # 从队列中取出一份内容, list类型
f = open("qiubai.txt","a")
for i in items:
json.dump(i, f, ensure_ascii=False, indent=2) # python对象转json文件
# f.write(json.dumps(i))
f.close()
self.content_queue.task_done() # 队列计数-1

def run(self):
# 1.获取url list
# url_list = self.get_total_url()
thread_list = [] # 线程池
thread_url = threading.Thread(target=self.get_total_url) # 创建生产url的线程
thread_list.append(thread_url)
#发送网络请求
for i in range(10):
thread_parse = threading.Thread(target=self.parse_url) # 创建请求url的线程
thread_list.append(thread_parse)
# 解析数据
thread_get_content = threading.Thread(target=self.get_content) # 创建解析响应的线程
thread_list.append(thread_get_content)
# 保存数据
thread_save = threading.Thread(target=self.save_items) # 创建保存内容的线程
thread_list.append(thread_save)

for t in thread_list:
t.setDaemon(True) #为每个进程设置为后台进程,效果是主进程退出子进程也会退出
t.start() #为了解决程序结束无法退出的问题

self.url_queue.join() #让主线程等待,所有的队列为空的时候才能退出
self.html_queue.join()
self.content_queue.join()

if __name__ == "__main__":
qiubai = Qiubai()
qiubai.run()
————————————————
版权声明:本文为CSDN博主「DeltaTime」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/bua200720411091/article/details/93378433

非结构化数据处理(文本)
正则
match 方法:从起始位置开始查找,一次匹配
# match 方法用于查找字符串的头部(也可以指定起始位置),它是一次匹配,只要找到了一个匹配的结果就返回,而不是查找所有匹配的结果。它的一般使用形式如下:
# 其中,string 是待匹配的字符串,pos 和 endpos 是可选参数,指定字符串的起始和终点位置,默认值分别是 0 和 len (字符串长度)。
# 不指定 pos 和 endpos 时,match 方法默认匹配字符串的头部。
#当匹配成功时,返回一个 Match 对象,如果没有匹配上,则返回 None。
import re
pattern = re.compile(r'\d+') # 用于匹配至少一个数字
m = pattern.match('one12twothree34four') # 查找头部,没有匹配
print (m)
m = pattern.match('one12twothree34four', 2, 10) # 从'e'的位置开始匹配,没有匹配
m = pattern.match('one12twothree34four', 3, 10) # 从'1'的位置开始匹配,正好匹配

group()分组匹配, 提取数据
pattern = re.compile(r'([a-z]+) ([a-z]+)', re.I) # re.I 表示忽略大小写
m = pattern.match('Hello World Wide Web')
print (m) # 匹配成功,返回一个 Match 对象
#<_sre.SRE_Match object at 0x10bea83e8>

m.group(0) # 返回匹配成功的整个子串
#'Hello World'
m.group(1) # 返回第一个分组匹配成功的子串
#'Hello'
m.group(2) # 返回第二个分组匹配成功的子串
#'World'
m.span(0) # 返回匹配成功的整个子串的索引
#(0, 11)
m.span(1) # 返回第一个分组匹配成功的子串的索引
#(0, 5)

search 方法:从任何位置开始查找,一次匹配
# search 方法用于查找字符串的任何位置,它也是一次匹配,只要找到了一个匹配的结果就返回,而不是查找所有匹配的结果,它的一般使用形式如下:
# 当匹配成功时,返回一个 Match 对象,如果没有匹配上,则返回 None。
pattern = re.compile('\d+')
m = pattern.search('one12twothree34four') # 这里如果使用 match 方法则不匹配
# <_sre.SRE_Match object at 0x10cc03ac0>
m.group()
# '12'

findall 方法:全部匹配,返回列表
# 搜索整个字符串,获得所有匹配的结果。
pattern = re.compile(r'\d+') # 查找数字
result1 = pattern.findall('hello 123456 789')
result2 = pattern.findall('one1two2three3four4', 0, 10)
print (result1)
print (result2)
# ['123456', '789']
# ['1', '2']

finditer 方法:全部匹配,返回迭代器

split 方法:分割字符串,返回列表

p = re.compile(r'[\s\,\;]+')
print (p.split('a,b;; c d'))
# ['a', 'b', 'c', 'd']

sub 方法:替换
p = 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'

xpath
表达式
nodename 选取此节点的所有子节点。
/ 从根节点选取。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。

路径
bookstore 选取 bookstore 元素的所有子节点。
/bookstore 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。
//book 选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang 选取名为 lang 的所有属性。

谓语
/bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。
/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()<3] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang=’eng’] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

多个路径
//book/title | //book/price 选取 book 元素的所有 title 和 price 元素。
//title | //price 选取文档中的所有 title 和 price 元素。
/bookstore/book/title | //price 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。

lxml库
使用xpath之前需要先将响应信息转换为xml结构
安装: pip install lxml
# 1. 从字符串中解析
# 使用 lxml 的 etree 库
from lxml import etree

text = '''
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a> # 注意,此处缺少一个 </li> 闭合标签
</ul>
</div>
'''
#利用etree.HTML,将字符串解析为HTML文档
# lxml可以修正缺少的html标签
html = etree.HTML(text)
# 按字符串序列化HTML文档
result = etree.tostring(html)
print(result)

# 2. 从文件中解析
from lxml import etree

# 读取外部文件 hello.html
html = etree.parse('./hello.html')
result = etree.tostring(html, pretty_print=True)
# 谓语 [last()] 可以找到最后一个元素
result = html.xpath('//li[last()]/a/@href') # 获取href属性的值
result = html.xpath('//li[last()-1]/a').text # 获取a标签的文本
result = html.xpath('//*[@class="bold"]').tag # 获取标签名
print(result)


CSS (beautifulsoup4)
lxml 只会局部遍历,而Beautiful Soup 是基于HTML DOM的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml。
安装: pip install beautifulsoup4
基本使用
from bs4 import BeautifulSoup

html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
#创建 Beautiful Soup 对象
# soup = BeautifulSoup(html)
soup = BeautifulSoup(html,'lxml') # 指定lxml解析器
# 打开本地 HTML 文件的方式来创建对象
# soup = BeautifulSoup(open('index.html'))
# 格式化输出 soup 对象的内容
print soup.prettify()

bs4 的4大对象种类
Tag 标签
# 获取各类标签对象
print soup.title
# <title>The Dormouse's story</title>
print soup.head
# <head><title>The Dormouse's story</title></head>
print soup.a
# <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
print soup.p
# <p class="title" name="dromouse"><b>The Dormouse's story</b></p>
print type(soup.p)
# <class 'bs4.element.Tag'>

# 对于 Tag,它有两个重要的属性,是 name 和 attrs
print soup.head.name # 标签的名字
# head
print soup.p.attrs # 标签的所有属性
# {'class': ['title'], 'name': 'dromouse'}

print soup.p['class'] # 获取标签的某一个属性的值
# soup.p.get('class') # 等价于上面
# ['title'] #还可以利用get方法,传入属性的名称,二者是等价的

BeautifulSoup 整体页面DOM对象, 它是一个特殊的Tag对象
print type(soup.name)
# <type 'unicode'>
print soup.name
# [document]
print soup.attrs # 文档本身的属性为空
# {}

NavigableString 标签的内容
print soup.p.string
# The Dormouse's story
print type(soup.p.string)
# In [13]: <class 'bs4.element.NavigableString'>

Comment 注释对象 它是一个特殊的NavigableString
print soup.a
# <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
print soup.a.string
# Elsie
print type(soup.a.string)
# <class 'bs4.element.Comment'>

bs4 搜索
直接子节点 :.contents .children
# .content 属性可以将tag的子节点以列表的方式输出
print soup.head.contents
# [<title>The Dormouse's story</title>]
print soup.head.contents[0]
# <title>The Dormouse's story</title>
print soup.head.children
#<listiterator object at 0x7f71457f5710>
for child in soup.body.children:
print child

find_all(name, attrs, recursive, text, **kwargs) 返回list
# 1. 通过标签名查找
soup.find_all('b') # 查找所有b标签, 返回列表
# [<b>The Dormouse's story</b>]

# 2. 通过属性查找
soup.find_all(id='link2')
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

select() 返回list
# 1. 通过标签名查找
print soup.select('title')
#[<title>The Dormouse's story</title>]

# 2. 通过类名查找
print soup.select('.sister')
# [<a class="sister" href="http://example.com/ </a>]

# 3. 通过id查找
print soup.select('#link1')
#[<a class="sister" href="http://example.com/elsie" id="link1">链接</a>]

# 4. 通过属性查找
print soup.select('a[class="sister"]')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- --></a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
print soup.select('a[href="http://example.com/elsie"]')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- --></a>]

# 5. 组合查找
print soup.select('p #link1')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- --></a>]

# 6. 获取内容
soup = BeautifulSoup(html, 'lxml')
print type(soup.select('title'))
print soup.select('title')[0].get_text()

for title in soup.select('title'):
print title.get_text()

结构化数据处理(json)
使用json模块进行json字符串与python对象之间相互转换的操作
json.loads() json 转 python
import json

strList = '[1, 2, 3, 4]' # json的数组
strDict = '{"city": "北京", "name": "大猫"}' # json的对象
json.loads(strList) # json字符串转python列表
# [1, 2, 3, 4]
json.loads(strDict) # json数据自动按Unicode存储
# {u'city': u'\u5317\u4eac', u'name': u'\u5927\u732b'}

json.dumps() python 转 json
import json
import chardet # chardet是一个非常优秀的编码识别模块,可通过pip安装

listStr = [1, 2, 3, 4]
tupleStr = (1, 2, 3, 4)
dictStr = {"city": "北京", "name": "大猫"}
json.dumps(listStr)
# '[1, 2, 3, 4]'
json.dumps(tupleStr)
# '[1, 2, 3, 4]'

# 注意:json.dumps() 序列化时默认使用的ascii编码
# 添加参数 ensure_ascii=False 禁用ascii编码,按utf-8编码
# chardet.detect()返回字典, 其中confidence是检测精确度
json.dumps(dictStr)
# '{"city": "\\u5317\\u4eac", "name": "\\u5927\\u5218"}'
chardet.detect(json.dumps(dictStr)) # 检测编码
# {'confidence': 1.0, 'encoding': 'ascii'}
print json.dumps(dictStr, ensure_ascii=False)
# {"city": "北京", "name": "大刘"}
chardet.detect(json.dumps(dictStr, ensure_ascii=False))
# {'confidence': 0.99, 'encoding': 'utf-8'}

json.load() 从文件中直接转python
读取文件中json形式的字符串元素 转化成python类型
strList = json.load(open("listStr.json"))
# [{u'city': u'\u5317\u4eac'}, {u'name': u'\u5927\u5218'}]
strDict = json.load(open("dictStr.json"))
# {u'city': u'\u5317\u4eac', u'name': u'\u5927\u5218'}

json.dump() python转json写入文件
将Python内置类型序列化为json对象后写入文件
listStr = [{"city": "北京"}, {"name": "大刘"}]
json.dump(listStr, open("listStr.json","w"), ensure_ascii=False)

dictStr = {"city": "北京", "name": "大刘"}
json.dump(dictStr, open("dictStr.json","w"), ensure_ascii=False)

xpath抓取案例
糗事百科
#coding=utf-8
import requests
from retrying import retry
from lxml import etree

class Qiubai_spider():
def __init__(self):
self.url = "http://www.qiushibaike.com/8hr/page/{}/" # 链接模型
self.headers = {
"User-Agent":"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1 Trident/5.0;"
}

@retry(stop_max_attempt_number=5) #调用retry,当assert出错时候,重复请求5次
def parse_url(self,url):
response = requests.get(url,timeout=10,headers=self.headers) #请求url
assert response.status_code==200 #当响应码不是200时候,做断言报错处理
print(url)
return etree.HTML(response.text) #返回etree之后的html

def parse_content(self,html):
item_temp = html.xpath("//div[@class='article block untagged mb15']")
print(len(item_temp))
for item in item_temp:
#获取用户头像地址
avatar = item.xpath("./div[1]/a[1]/img/@src")[0] if len(item.xpath("./div[1]/a[1]/img/@src"))>0 else None
#为头像地址添加前缀
if avatar is not None and not avatar.startswith("http:"):
avatar = "http:"+avatar
name = item.xpath("./div[1]/a[2]/h2/text()")[0] #获取用户名
content = item.xpath("./a[@class='contentHerf']/div/span/text()")[0] #获取内容
star_number = item.xpath("./div[@class='stats']/span[1]/i/text()")[0] #获取点赞数
comment_number = item.xpath("./div[@class='stats']/span[2]/a/i/text()")[0] #获取评论数
print("*"*100)

def run(self):
'''函数的主要逻辑实现
'''
url = self.url.format(1) #获取到url
html = self.parse_url(url) #请求url
self.parse_content(html) #解析页面内容并把内容存入内容队列

if __name__ == "__main__":
qiubai = Qiubai_spider()
qiubai.run()


天气网
#encoding: utf-8

import requests
from bs4 import BeautifulSoup
from pyecharts import Bar

ALL_DATA = []

def parse_page(url):
headers = {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"
}
response = requests.get(url,headers=headers)
text = response.content.decode('utf-8')
# html5lib
# pip install html5lib
soup = BeautifulSoup(text,'html5lib')
conMidtab = soup.find('div',class_='conMidtab')
tables = conMidtab.find_all('table')
for table in tables:
trs = table.find_all('tr')[2:]
for index,tr in enumerate(trs):
tds = tr.find_all('td')
city_td = tds[0]
if index == 0:
city_td = tds[1]
city = list(city_td.stripped_strings)[0]
temp_td = tds[-2]
min_temp = list(temp_td.stripped_strings)[0]
ALL_DATA.append({"city":city,"min_temp":int(min_temp)})
# print({"city":city,"min_temp":int(min_temp)})

def main():
urls = [
'http://www.weather.com.cn/textFC/hb.shtml',
'http://www.weather.com.cn/textFC/db.shtml',
'http://www.weather.com.cn/textFC/hd.shtml',
'http://www.weather.com.cn/textFC/hz.shtml',
'http://www.weather.com.cn/textFC/hn.shtml',
'http://www.weather.com.cn/textFC/xb.shtml',
'http://www.weather.com.cn/textFC/xn.shtml',
'http://www.weather.com.cn/textFC/gat.shtml'
]
for url in urls:
parse_page(url)

# 分析数据
# 根据最低气温进行排序
ALL_DATA.sort(key=lambda data:data['min_temp'])

data = ALL_DATA[0:10]
cities = list(map(lambda x:x['city'],data))
temps = list(map(lambda x:x['min_temp'],data))
# pyecharts
# pip install pyecharts
chart = Bar("中国天气最低气温排行榜")
chart.add('',cities,temps)
chart.render('temperature.html')


if __name__ == '__main__':
main()
# ALL_DATA = [
# {"city": "北京", 'min_temp': 0},
# {"city": "天津", 'min_temp': -8},
# {"city": "石家庄", 'min_temp': -10}
# ]
#
# ALL_DATA.sort(key=lambda data:data['min_temp'])
# print(ALL_DATA)

多线程
Queue 常用方法
Queue是python中的标准库,可以直接import Queue引用;队列是线程间最常用的交换数据的形式
Queue是线程安全的
Queue.qsize() # 返回队列的大小
Queue.empty() # 如果队列为空,返回True,反之False
Queue.full() # 如果队列满了,返回True,反之False
Queue.full # 与 maxsize 大小对应
Queue.get([block[, timeout]]) #获取队列,timeout等待时间

创建队列
import Queue
myqueue = Queue.Queue(maxsize = 10)

插入队列
myqueue.put(10)

将一个值从队列中取出
myqueue.get()

多线程抓取案例
# coding=utf-8
import requests
from lxml import etree
import json
from queue import Queue
import threading

class Qiubai:
def __init__(self):
self.headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWeb\
Kit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"}
self.url_queue = Queue() # url队列
self.html_queue =Queue() # html响应信息队列
self.content_queue = Queue() # 内容队列


def get_total_url(self):
'''
获取了所有的页面url,并且返回urllist
return :list
'''
url_temp = 'https://www.qiushibaike.com/8hr/page/{}/' # url模板
for i in range(1,36):
self.url_queue.put(url_temp.format(i)) # url入队

def parse_url(self):
'''
一个发送请求,获取响应,同时etree处理html
'''
while self.url_queue.not_empty: # 判断url队列不为空
url = self.url_queue.get() # 从队列中取出一个url
print("parsing url:", url)
response = requests.get(url,headers=self.headers,timeout=10) # 发送请求
html = response.content.decode() #获取html字符串
html = etree.HTML(html) #获取element 类型的html
self.html_queue.put(html) # html响应入队
self.url_queue.task_done() # task_done的时候,队列计数减一

def get_content(self):
'''
:param url:
:return: 一个list,包含一个url对应页面的所有段子的所有内容的列表
'''
while self.html_queue.not_empty: # 判断html响应队列不为空
html = self.html_queue.get() # 从队列中取出一份html响应信息
# 解析内容
total_div = html.xpath('//div[@class="article block untagged mb15"]') #返回divelememt的一个列表
itme_list = [] # 用于保存最终所有的信息
for i in total_div: #遍历div标枪,获取糗事百科每条的内容的全部信息
author_img = i.xpath('./div[@class="author clearfix"]/a[1]/img/@src')
author_img = "https:" + author_img[0] if len(author_img) > 0 else None
author_name = i.xpath('./div[@class="author clearfix"]/a[2]/h2/text()')
author_name = author_name[0] if len(author_name) > 0 else None
author_href = i.xpath('./div[@class="author clearfix"]/a[1]/@href')
author_href = "https://www.qiushibaike.com" + author_href[0] if len(author_href) > 0 else None
author_gender = i.xpath('./div[@class="author clearfix"]//div/@class')
author_gender = author_gender[0].split(" ")[-1].replace("Icon", "") if len(author_gender) > 0 else None
author_age = i.xpath('./div[@class="author clearfix"]//div/text()')
author_age = author_age[0] if len(author_age) > 0 else None
content = i.xpath('./a[@class="contentHerf"]/div/span/text()')
content_vote = i.xpath('./div[@class="stats"]/span[1]/i/text()')
content_vote = content_vote[0] if len(content_vote) > 0 else None
content_comment_numbers = i.xpath('./div[@class="stats"]/span[2]/a/i/text()')
content_comment_numbers = content_comment_numbers[0] if len(content_comment_numbers) > 0 else None
hot_comment_author = i.xpath('./a[@class="indexGodCmt"]/div/span[last()]/text()')
hot_comment_author = hot_comment_author[0] if len(hot_comment_author) > 0 else None
hot_comment = i.xpath('./a[@class="indexGodCmt"]/div/div/text()')
hot_comment = hot_comment[0].replace("\n:", "").replace("\n", "") if len(hot_comment) > 0 else None
hot_comment_like_num = i.xpath('./a[@class="indexGodCmt"]/div/div/div/text()')
hot_comment_like_num = hot_comment_like_num[-1].replace("\n", "") if len(hot_comment_like_num) > 0 else None
item = dict(
author_name=author_name,
author_img=author_img,
author_href=author_href,
author_gender=author_gender,
author_age=author_age,
content=content,
content_vote=content_vote,
content_comment_numbers=content_comment_numbers,
hot_comment=hot_comment,
hot_comment_author=hot_comment_author,
hot_comment_like_num=hot_comment_like_num
)
item_list.append(item)
self.content_queue.put(items) # 内容入队
self.html_queue.task_done() # task_done的时候,队列计数-1

def save_items(self):
'''
保存items
:param items:列表
'''
while self.content_queue.not_empty: # 判断内容队列不为空
items = self.content_queue.get() # 从队列中取出一份内容, list类型
f = open("qiubai.txt","a")
for i in items:
json.dump(i, f, ensure_ascii=False, indent=2) # python对象转json文件
# f.write(json.dumps(i))
f.close()
self.content_queue.task_done() # 队列计数-1

def run(self):
# 1.获取url list
# url_list = self.get_total_url()
thread_list = [] # 线程池
thread_url = threading.Thread(target=self.get_total_url) # 创建生产url的线程
thread_list.append(thread_url)
#发送网络请求
for i in range(10):
thread_parse = threading.Thread(target=self.parse_url) # 创建请求url的线程
thread_list.append(thread_parse)
# 解析数据
thread_get_content = threading.Thread(target=self.get_content) # 创建解析响应的线程
thread_list.append(thread_get_content)
# 保存数据
thread_save = threading.Thread(target=self.save_items) # 创建保存内容的线程
thread_list.append(thread_save)

for t in thread_list:
t.setDaemon(True) #为每个进程设置为后台进程,效果是主进程退出子进程也会退出
t.start() #为了解决程序结束无法退出的问题

self.url_queue.join() #让主线程等待,所有的队列为空的时候才能退出
self.html_queue.join()
self.content_queue.join()

if __name__ == "__main__":
qiubai = Qiubai()
qiubai.run()
————————————————
版权声明:本文为CSDN博主「DeltaTime」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/bua200720411091/article/details/93378433

标签:提取,author,url,self,soup,html,print,数据
来源: https://www.cnblogs.com/llflifei/p/11910651.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有