永續報告書內容分析
真理大學微學分課程
智慧健康永續學分系列課程-ESG投資
Sustainability Reports
CSV檔內有連結
Content Analysis with AI
資料收集
資料分析與結果呈現
資料爬蟲
資料爬蟲
資料清理
前處理
內容分析
視覺化
資料分析
資料爬蟲
資料清理
文章
哪些可能的任務?
情感分析 (Sentiment Analysis)
主題分析 (Topic Analysis/ Thematic Analysis)
新聞分類 (News Categorization)
多標籤分類(Multilabel Classification)
問答 (Question Answering)
自然語言推論 (Natural Language Inference )
自動摘要 (automatic Abstracting)
機器翻譯 (Machine Translation)
情感分析(Sentiment Analysis)
負面評價Negative
這家店的售後服務完全不行
餐點還不錯啦,但是還有改進空間
中性評價neutral
中性評價neutral
正面評價positive
很讚喔,超喜歡你們家的產品
分類問題
情感分析
編碼
輸入層
隱藏層
輸出層
輸出結果分類
真實
政治宣傳
惡搞
反諷
假新聞
帶風向
片面資訊
新聞分類
分類問題
機器翻譯
迴歸問題
Transformer
輸入英文: milk drink I
輸出法文: Je bois du lait <eos>
編碼器
解碼器
編碼器輸出
解碼器輸入
<sos>: 開始符號
<eos>: 結束符號
文本生成
輸入: 一句話
輸出: 一篇短文!
經過多個語言模型...
迴歸問題
圖片來源1
圖片來源2
2.1 遍訪連結
建立待訪堆疊
1. 從一個網頁開始
2.2 擷取內容
3. 輸出結果(至檔案)
取得HTML回應碼
解析超連結
解析其他HTML元素
資料輸出
資料清理
能力1: 取得HTML回應碼?了解HTTP協定
網址(http request)
瀏覽器
Web 伺服器
HTML回應碼(http response)
http連線功能
➠
能力2: 解析超連結、其他HTML元素、網頁內容 ➠
HTML Parser(解析器)
以p元素為例
找出超連結元素A,屬性href的值 ➠ 待訪網址
元素
內容
開始標籤
結束標籤
屬性名稱
屬性值
能力2: 解析超連結、其他HTML元素、網頁內容
Parser(解析器)
能力1: 取得HTML回應碼?了解HTTP協定
http連線功能
➠
圖片來源1
能力3: 資料清理、資料輸出 ➠
資料處理工具
2.1 遍訪連結
建立待訪堆疊
1. 從一個網頁開始
2.2 擷取內容
3. 輸出結果(至檔案)
圖片來源1
圖片來源2
取得HTML回應碼
解析超連結
解析其他HTML元素
資料輸出
資料清理
☛ 單一頁面版:使用 Requests + BeautilfulSoup4
無限循環版:使用Scraper
pip install requests
pip install beautifulsoup4
pip install lxml
安裝Requests, BeautilfulSoup套件(使用pip 或 pip3)
parser部分使用lxml(速度快,也可解析XML檔)
若系統找不到pip,改用pip3
import requests
from bs4 import BeautifulSoup
# 假設html為你的HTML頁面內容
html = """
<html>
<head>
<title>示例頁面</title>
</head>
<body>
<div class="content">
<p>這是一個示例頁面。</p>
<div id="unique_id">
<h1>特定ID的內容</h1>
<p>這是要抓取的內容。</p>
</div>
<div class="other">
<p>其他內容。</p>
</div>
</div>
</body>
</html>
"""
# 使用BeautifulSoup解析HTML內容
soup = BeautifulSoup(html, 'html.parser')
# 找到特定ID為"unique_id"的元素
specific_element = soup.find(id='unique_id')
# 輸出特定ID為"unique_id"的元素內容
print(specific_element)
example1.py
beautifulsoup4 抓特定id
import requests
from bs4 import BeautifulSoup
# 假設html為你的HTML頁面內容
html = """
<html>
<head>
<title>示例頁面</title>
</head>
<body>
<div class="content">
<p>這是一個示例頁面。</p>
<div id="unique_id">
<h1>特定ID的內容</h1>
<p>這是要抓取的內容。</p>
</div>
<div class="other">
<p>其他內容。</p>
</div>
</div>
</body>
</html>
"""
# 使用BeautifulSoup解析HTML內容
soup = BeautifulSoup(html, 'html.parser')
# 找到特定ID為"unique_id"的元素
specific_element = soup.find(id='unique_id')
# 輸出特定ID為"unique_id"的元素內容
if specific_element:
content = specific_element.text # 或者使用 specific_element.get_text()
print(content)
else:
print("未找到特定元素")
example2.py
beautifulsoup4 抓特定id後
印出內文
import requests
from bs4 import BeautifulSoup
# 假設html為你的HTML頁面內容
html = """
<html>
<head>
<title>示例頁面</title>
</head>
<body>
<div class="content">
<p>這是一個示例頁面。</p>
<div class="specific_class">
<h1>特定Class的內容 1</h1>
<p>這是要抓取的內容 1。</p>
</div>
<div class="specific_class">
<h1>特定Class的內容 2</h1>
<p>這是要抓取的內容 2。</p>
</div>
<div class="other_class">
<p>其他內容。</p>
</div>
</div>
</body>
</html>
"""
# 使用BeautifulSoup解析HTML內容
soup = BeautifulSoup(html, 'html.parser')
# 找到具有特定class值為"specific_class"的元素
specific_elements = soup.find_all(class_='specific_class')
# 輸出符合特定class值的元素內容
for element in specific_elements:
print(element.get_text())
example3.py
beautifulsoup4 抓特定class
import requests
from bs4 import BeautifulSoup
# 假設html為你的HTML頁面內容
html = """
<html>
<head>
<title>示例頁面</title>
</head>
<body>
<div class="content">
<p>這是一個示例頁面。</p>
<a href="https://example.com/page1">連結到頁面1</a>
<a href="https://example.com/page2">連結到頁面2</a>
<a href="https://example.com/page3">連結到頁面3</a>
<div class="other">
<p>其他內容。</p>
<a href="https://example.com/page4">連結到頁面4</a>
</div>
</div>
</body>
</html>
"""
# 使用BeautifulSoup解析HTML內容
soup = BeautifulSoup(html, 'html.parser')
# 找到所有的超連結(<a>標籤)
all_links = soup.find_all('a')
# 輸出所有超連結的URL
for link in all_links:
print(link.get('href'))
example4.py
beautifulsoup4 抓所有class
import requests
from bs4 import BeautifulSoup
#所要擷取的網站網址
url = 'https://www.ptt.cc/bbs/Mobile-game/M.1700108779.A.D0A.html'
#建立回應
response = requests.get(url)
#印出網站原始碼
#print(response.text)
#將原始碼做整理
soup = BeautifulSoup(response.text, 'lxml')
# 找到具有特定class值的元素
specific_elements = soup.find_all(class_='push')
# 輸出符合特定class值的元素内容
for element in specific_elements:
print(element.get_text())
ptt02.py
beautifulsoup4 抓所有class為push的元素
import requests
from bs4 import BeautifulSoup
#所要擷取的網站網址
url = 'https://www.ptt.cc/bbs/Mobile-game/index.html'
base_url = 'https://www.ptt.cc'
#建立回應
response = requests.get(url)
#將原始碼做整理
soup = BeautifulSoup(response.text, 'lxml')
# 找到所有超連結
all_links = soup.find_all('a')
# 印出超連結
for link in all_links:
href = link.get('href')
if href==None:
continue
if "http://" in href or "https://" in href:
print(href)
else:
print(base_url+href)
ptt03.py
beautifulsoup4 抓所有超連結
import requests
from bs4 import BeautifulSoup
# Step 1: 發出Http Request, 解析(parse)Http Response
## url: 要擷取的網址
base_url = "http://twbsball.dils.tku.edu.tw" # 網站基礎網址,補絕對網址用
url = "http://twbsball.dils.tku.edu.tw/wiki/index.php/%E5%88%86%E9%A1%9E:%E5%8F%B0%E7%81%A3%E7%90%83%E5%93%A1"
resp = requests.get(url) # 發出HTTP request
if resp.status_code == 200: # 代碼200表示連線正常
soup = BeautifulSoup(resp.content, "lxml") # parsing網頁內容
else:
exit(1) # 非200代碼,程式直接結束
### 維基百科頁面標題特徵: id="firstHeading"
title = soup.find(id="firstHeading") # 根據id值進行搜尋
print(f'頁面標題{title.text}')
### 維基百科頁面內容:假設只對包含在id="mw-pages"內的超連結感興趣
### 注意!!!每個網頁內容的id可能都不一樣!!!必須先詳細了解網頁格式
all_links = soup.find(id="mw-pages").find_all("a") # 找出所有<A>標籤
print(f"超連結數量:{len(all_links):10}")
# 走訪過濾後的<A>標籤:
#### 1. 只對此Wiki站內連結感興趣
#### 2. 只處理球員連結
num_of_link = 0
for link in all_links:
if link['href'].find("/wiki/") == -1: # 站外連結跳過不處理
continue
if link.text == '之後200': # 網頁特定內容,跳過不處理
print(f'type of link:{type(link)}')
continue
print(f"球員:{link.text} 網址:{base_url + link['href']}")
num_of_link += 1
print(f'球員總數{num_of_link:10}')
webscraping-link.py
重要:必須先了解該網頁的HTML結構
重點觀察
前往目標網址後,開啟「開發人員工具」(F12)
任務 1: 爬取「球員連結」
但不是「所有超連結」都是球員
移動游標尋找目標區域
div標籤, id值 "mw-pages"
# 以'lxml' parser解析 resp.content的內容
soup = BeautifulSoup(resp.content, "lxml")
...
# 找出mw-pages段落裡面所有<A>標籤
all_links = soup.find(id="mw-pages").find_all("a")
任務 2: 擷取「頁面內容」
有興趣的段落沒有 id, class等屬性值可用
dl, dd, ul, li等標籤無法區分是否為感興趣的段落
url = "擷取的網址"
resp = requests.get(url) # (1)連線
if resp.status_code == 200: # 代碼200表示連線正常
soup = BeautifulSoup(resp.content, "lxml") # (2)分析
else:
exit(1) # 非200代碼,程式直接結束
import requests
from bs4 import BeautifulSoup
import需要的模組
連線與分析(parsing)
### 維基百科頁面標題特徵: id="firstHeading"
title = soup.find(id="firstHeading") # 根據id值進行搜尋
print(f'頁面標題{title.text}')
擷取任務1: 取得標題
### 維基百科頁面內容:假設只對包含在id="mw-pages"內的超連結感興趣
### 注意!!!每個網頁內容的id可能都不一樣!!!必須先詳細了解網頁格式
all_links = soup.find(id="mw-pages").find_all("a") #找出所有<A>標籤
# 走訪過濾後的<A>標籤:
for link in all_links:
if link['href'].find("/wiki/") == -1: # 站外連結跳過不處理
continue
if link.text == '之後200': # 網頁特定內容,跳過不處理
continue
print(f"球員:{link.text} 網址:{link['href']}") # 印出網址
擷取任務2: 取出段落中的超連結
單一頁面版:使用 Requests + BeautilfulSoup4
☛ 無限循環版:使用Scrapy
安裝Scrapy, BeautilfulSoup套件(使用pip 或 pip3)
parser部分使用lxml(速度快,也可解析XML檔)
pip install scrapy
pip install beautifulsoup4
pip install lxml
若系統找不到pip,改用pip3
此範例使用
效能較佳!
建立Scrapy專案,例如:
scrapy startproject tutorial
你的程式加在這個資料夾內
scrapy startproject mycrawler
在這個資料夾內新增
twbaseball_spider.py(任何檔名皆可)
爬蟲主程式#1
資料夾內可以手動新增多隻爬蟲主程式
scrapy genspider CSRSpider https://cgc.twse.com.tw/
生成
CSRSpider.py
CSRSpider.py
import scrapy
class CsrspiderSpider(scrapy.Spider):
name = 'CSRSpider'
allowed_domains = ['cgc.twse.com.tw']
start_urls = ['https://cgc.twse.com.tw/']
def parse(self, response):
pass
CSRSpider.py
1. 修改start_urls
2. 加入parse()程式碼
3. 終端機中執行
import scrapy
class TwBaseballSpider(scrapy.Spider): # 1. 繼承scrapy.Spider
name = "quotes" # 2. 為spider取名
start_urls = [ # 3. 頁面擷取串列: 數量不限
'https://quotes.toscrape.com/page/1/', # 爬取網址1
'https://quotes.toscrape.com/page/2/', # 爬取網址2
]
def parse(self, response): # 4. 頁面解析
page = response.url.split("/")[-2] # 4-1 內容解析
# 4-2 資料儲存
filename = f'quotes-{page}.html' # 設定檔名
with open(filename, 'wb') as f:
f.write(response.body) # 寫入網頁<body>內容
self.log(f'Saved file {filename}')
twbaseball_spider.py
爬取兩個網頁,將網頁內容寫入檔案
import scrapy
class TwBaseballSpider(scrapy.Spider): # 1. 繼承scrapy.Spider
name = "quotes" # 2. 為spider取名
start_urls = [ # 3. 頁面擷取串列: 數量不限
'https://quotes.toscrape.com/page/1/', # 爬取網址1
'https://quotes.toscrape.com/page/2/', # 爬取網址2
]
def parse(self, response): # 4. 頁面解析
page = response.url.split("/")[-2] # 4-1 內容解析
# 4-2 資料儲存
filename = f'quotes-{page}.html' # 設定檔名
with open(filename, 'wb') as f:
f.write(response.body) # 寫入網頁<body>內容
self.log(f'Saved file {filename}')
twbaseball_spider.py
class名稱自取
import scrapy
class TwBaseballSpider(scrapy.Spider): # 1. 繼承scrapy.Spider
name = "quotes" # 2. 為spider取名
start_urls = [ # 3. 頁面擷取串列: 數量不限
'https://quotes.toscrape.com/page/1/', # 爬取網址1
'https://quotes.toscrape.com/page/2/', # 爬取網址2
]
def parse(self, response): # 4. 頁面解析
page = response.url.split("/")[-2] # 4-1 內容解析
# 4-2 資料儲存
filename = f'quotes-{page}.html' # 設定檔名
with open(filename, 'wb') as f:
f.write(response.body) # 寫入網頁<body>內容
self.log(f'Saved file {filename}')
twbaseball_spider.py
為spider取名
執行程式時,會指定此名稱
import scrapy
class TwBaseballSpider(scrapy.Spider): # 1. 繼承scrapy.Spider
name = "quotes" # 2. 為spider取名
start_urls = [ # 3. 頁面擷取串列: 數量不限
'https://quotes.toscrape.com/page/1/', # 爬取網址1
'https://quotes.toscrape.com/page/2/', # 爬取網址2
]
def parse(self, response): # 4. 頁面解析
page = response.url.split("/")[-2] # 4-1 內容解析
# 4-2 資料儲存
filename = f'quotes-{page}.html' # 設定檔名
with open(filename, 'wb') as f:
f.write(response.body) # 寫入網頁<body>內容
self.log(f'Saved file {filename}')
twbaseball_spider.py
執行時,依序爬取網頁
但不限於只爬取串列中指定的
網頁
可遞迴呼叫「回呼函式」parse()
達成無限循環
import scrapy
class TwBaseballSpider(scrapy.Spider): # 1. 繼承scrapy.Spider
name = "quotes" # 2. 為spider取名
start_urls = [ # 3. 頁面擷取串列: 數量不限
'https://quotes.toscrape.com/page/1/', # 爬取網址1
'https://quotes.toscrape.com/page/2/', # 爬取網址2
]
def parse(self, response): # 4. 頁面解析
page = response.url.split("/")[-2] # 4-1 內容解析
# 4-2 資料儲存
filename = f'quotes-{page}.html' # 設定檔名
with open(filename, 'wb') as f:
f.write(response.body) # 寫入網頁<body>內容
self.log(f'Saved file {filename}')
twbaseball_spider.py
回呼函式
透過response參數解析內容
繼續跟進處理其他網址
回傳擷取的資料、儲存...
cd mycrawler
從「終端機」執行
1. 切換到專案資料最上層!
scrapy crawl quotes
scrapy crawl twbaseball
2. 執行scrapy crawl指令
spider名稱,自訂於程式碼中
import scrapy
class TwBaseballSpider(scrapy.Spider): # 1. 繼承scrapy.Spider
name = "quotes" # 2. 為spider取名
start_urls = [ # 3. 頁面擷取串列: 數量不限
'https://quotes.toscrape.com/page/1/', # 爬取網址1
'https://quotes.toscrape.com/page/2/', # 爬取網址2
]
def parse(self, response): # 4. 頁面解析
page = response.url.split("/")[-2] # 4-1 內容解析
# 4-2 資料儲存
filename = f'quotes-{page}.html' # 設定檔名
with open(filename, 'wb') as f:
f.write(response.body) # 寫入網頁<body>內容
self.log(f'Saved file {filename}')
...
# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
#'sreport.middlewares.SreportDownloaderMiddleware': 543,
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
'scrapy_user_agents.middlewares.RandomUserAgentMiddleware': 400,
}
...
爬取內容時改變不同的User-Agent值(HTTP header設定)
找到專案內的settings.py
1. 開啟DOWNLOADER_MIDDLEWARES設定
2. 第5行註解掉
3. 加入6,7行
若使用相同的User-Agent值會被封鎖
pip install scrapy-user-agents
<html>
<head>
<title>示例頁面</title>
</head>
<body>
<div id="content">
<p>這是一個示例頁面。 </p>
<div id="specific_id">
<h1>特定ID的內容</h1>
<p>這是要找的內容。 </p>
</div>
<div id="other_id">
<p>其他內容。 </p>
</div>
</div>
</body>
</html>
http://example.com/example.html
範例頁面html程式碼
import scrapy
class MySpider(scrapy. Spider):
name = 'my_spider'
# 抓取的網址:與上一頁url名稱相同
start_urls = ['http://example.com/example.html']
def parse(self, response):
# 使用CSS選擇器找到id為“specific_id”的元素
# '#'字號代表html id值
specific_element = response.css('#specific_id')
# 處理找到的元素
for element in specific_element:
# 輸出元素內容或其他操作
self.logger.info("Found element: %s", element.get())
# 或是
print(f'找到元素{element.get()}')
MySpider.py
抓特定id後印出內文
import scrapy
class MySpider(scrapy. Spider):
name = 'my_spider'
def parse(self, response):
# 使用 CSS 選擇器找到特定 ul 元素
# 替換為你的 <ul> 元素的 ID 或其他屬性選擇器
ul_element = response.css('ul#your_ul_id')
# 找到特定 ul 元素下的所有 li 元素
li_elements = ul_element.css('li')
# 處理找到的 li 元素
for li in li_elements:
# 在這裡進行處理
# 例如,列印 li 內容
self.logger.info("Found li: %s", li.get())
MySpider.py
import scrapy
class MySpider(scrapy.Spider):
name = 'my_spider'
start_urls = ['https://example.com'] # 你要爬取的網頁 URL
def parse(self, response):
# 使用 CSS 選擇器找到特定 class 值的元素
elements = response.css('.your_class_name')
# 處理找到的元素
for element in elements:
# 在這裡進行處理
# 例如,列印元素內容
self.logger.info("Found element: %s", element.get())
# 或者
print(f'找到元素: {element.get()}')
MySpider.py
抓特定id後印出內文
import scrapy
class CsrspiderSpider(scrapy.Spider):
name = 'CSRSpider'
# allowed_domains = ['cgc.twse.com.tw']
start_urls = ['https://cgc.twse.com.tw/corpSocialResponsibility/chPage']
def parse(self, response):
#先找到table <table class="table-rwd">
table = response.css('table.table-rwd')
# 找每一行
rows = table.css('tr') # <tr>...</tr>
print(f'找到{len(rows)}行')
for row in rows:
cells = row.css('td, th') # tr內的所有td或th元素
# data串列:每一個td或th元素
data = [cell.css('::text').get() for cell in cells]
print(f'行:{data}')
CSRSpider.py 抓table
import scrapy
class CsrspiderSpider(scrapy.Spider):
name = 'CSRSpider'
# allowed_domains = ['cgc.twse.com.tw']
start_urls = ['https://cgc.twse.com.tw/corpSocialResponsibility/chPage']
def parse(self, response):
#先找到table <table class="table-rwd">
table = response.css('table.table-rwd')
# 找每一行
rows = table.css('tr') # <tr>...</tr>
print(f'找到{len(rows)}行')
for row in rows:
cells = row.css('td, th') # tr內的所有td或th元素
# data串列:每一個td或th元素
data = [cell.css('::text').get() for cell in cells]
for cell in cells:
link = cell.css('a::attr(href)').get()
if not link:
continue
data.append(link)
print(f'行:{data}')
CSRSpider.py 抓table
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Listing Page</title>
</head>
<body>
<h1>Listing Page</h1>
<ul>
<li><a href="https://example.com/page1">Page 1</a></li>
<li><a href="https://example.com/page2">Page 2</a></li>
<li><a href="https://example.com/page3">Page 3</a></li>
<!-- 更多的链接 -->
</ul>
</body>
</html>
HTML頁面 https://example.com/listing
import scrapy
class LinksSpider(scrapy. Spider):
name = 'links_spider'
# 起始URL
start_urls = ['https://example.com/listing']
def parse(self, response):
# 取得目前頁面的所有連結
links = response.css('a::attr(href)').getall()
# 處理頁面的連結
for link in links:
yield scrapy.Request(
response.urljoin(link),
callback=self.parse
)
LinkSpider.py
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Listing Page</title>
</head>
<body>
<h1>Listing Page</h1>
<ul class="listing">
<li><a href="https://example.com/page1">Page 1</a></li>
<li><a href="https://example.com/page2">Page 2</a></li>
<li><a href="https://example.com/page3">Page 3</a></li>
<!-- 更多的链接 -->
</ul>
<div class="pagination">
<span class="current">1</span>
<a href="https://example.com/listing?page=2">2</a>
<a href="https://example.com/listing?page=3">3</a>
<!-- 更多的分页链接 -->
</div>
</body>
</html>
分頁範例 https://example.com/listing
import scrapy
class MySpider(scrapy.Spider):
name = 'my_spider'
def start_requests(self):
base_url = 'https://example.com/listing?page={}' # 基礎URL
total_pages = 5 # 假設總共有5頁
# 生成要訪問的每個分頁的URL
for page in range(1, total_pages + 1):
url = base_url.format(page)
yield scrapy.Request(url, self.parse)
def parse(self, response):
# 在這裡解析每個頁面的內容,如提取標題、內容等
# 你可以使用CSS選擇器或XPath來提取資料
titles = response.css('h2::text').getall()
contents = response.css('.content::text').getall()
# 處理提取的資料
for title, content in zip(titles, contents):
yield {
'Title': title,
'Content': content
}
MySpider.py
<div class="article">
<h1 class=“title”>文章標題</h1>
<div class=“author”>作者:John Doe</div>
<div class=“time”>發佈時間:2023-01-01</div>
<div class="content">
<p>文章內容... </p>
</div>
<div class="comments">
<div class="comment">
<div class=“comment-author”>評論作者:Alice</div>
<div class=“comment-content”>評論內容... </div>
</div>
<!-- 更多評論 -->
</div>
</div>
article.html
import scrapy
class ArticleSpider(scrapy. Spider):
name = 'article_spider'
start_urls=['文章所在網址']
def parse_article(self, response):
# 提取文章資訊
title = response.css('h1.title::text').get()
author = response.css('div.author::text').get()
time = response.css('div.time::text').get()
content = response.css('div.content p::text').getall()
# 資料清理pandas, numpy,如果需要的話
# ...
# 提取評論資訊
comments = []
# div元素之class="comments" 或 class="comment"
comment_divs = response.css('div.comments div.comment')
for comment_div in comment_divs:
# div元素 class="comment-author"。 "::text"指 div所包內文
comment_author = comment_div.css('div.comment-author::text').get()
comment_content = comment_div.css('div.comment-content::text').get()
# 資料清理pandas, numpy,如果需要的話
# ...
# 將此輪擷取資料加入串列
comments.append({
'author': comment_author,
'content': comment_content
})
# 傳回提取的文章資訊和評論資訊
# 注意:yield類似return,但較不花系統資源
yield {
'title': title,
'author': author,
'time': time,
'content': content,
'comments': comments
}
def parse(self, response):
# 取得目前頁面的所有連結,"a::attr(屬性名稱)"讀取元素<a>的某個屬性
# get()抓取一個, getall()抓取全部
links = response.css('a::attr(href)').getall()
# 處理目前頁面的連結並解析文章內容
for link in links:
yield scrapy.Request(response.urljoin(link), callback=self.parse_article)
# 取得分頁資訊
next_page = response.css('a.next-page::attr(href)').get()
if next_page:
yield scrapy.Request(response.urljoin(next_page), callback=self.parse)
ArticleSpider.py
抓特定id後印出內文
單一頁面版:使用 Requests + BeautilfulSoup4
☛ 無限循環版:使用Scrapy
import scrapy
from bs4 import BeautifulSoup
import random
import mycrawler.items as items
class TwBaseballSpider(scrapy.Spider): # 1. 繼承scrapy.Spider
name = "twbaseball" # 2. 為spider命名
allowed_domains = ['dils.tku.edu.tw']
start_urls = [ # 3. 頁面擷取串列
'http://twbsball.dils.tku.edu.tw/wiki/index.php/%E5%88%86%E9%A1%9E:%E5%8F%B0%E7%81%A3%E7%90%83%E5%93%A1'
]
....
twbaseball_spider.py(部份)
所有spider都需要這三部份
import scrapy
from bs4 import BeautifulSoup
import random
import mycrawler.items as items
class TwBaseballSpider(scrapy.Spider): # 1. 繼承scrapy.Spider
name = "twbaseball" # 2. 為spider命名
allowed_domains = ['dils.tku.edu.tw']
start_urls = [ # 3. 頁面擷取串列
'http://twbsball.dils.tku.edu.tw/wiki/index.php/%E5%88%86%E9%A1%9E:%E5%8F%B0%E7%81%A3%E7%90%83%E5%93%A1'
]
total_page = len(start_urls) # 分頁總數
num_of_player = 0 # 抓取的球員總數
player = items.Player() # 球員資料欄位->自行定義在items.py
sid = { # 台灣棒球維基館的section id
'簡介': '.E7.B0.A1.E4.BB.8B',
'基本資料': '.E5.9F.BA.E6.9C.AC.E8.B3.87.E6.96.99',
'經歷': '.E7.B6.93.E6.AD.B7',
'個人年表': '.E5.80.8B.E4.BA.BA.E5.B9.B4.E8.A1.A8',
'特殊事蹟': '.E7.89.B9.E6.AE.8A.E4.BA.8B.E8.B9.9F',
'職棒生涯成績': '.E8.81.B7.E6.A3.92.E7.94.9F.E6.B6.AF.E6.88.90.E7.B8.BE',
'外部連結': '.E5.A4.96.E9.83.A8.E9.80.A3.E7.B5.90'
}
def parse(self, response): # 4. 頁面解析
next200 = False # pagination link processing flag
player_links = [] # 球員超連結串列
for link in response.css('#mw-pages').css("a"):
text = link.css('a::text').get() # 超連結的文字部分
if text == '先前200': # 先前200已經處理過了
continue
if text == '之後200': # 之後200 下一頁球員
if next200: # 已經處理過了
continue
else: # 尚未處理
next200 = True # 標示為已處理
self.total_page += 1
# yield response.follow(link, self.parse) # 連續循環版
print(f"^^^current total pages:{self.total_page}")
if random.randint(1,100)<=2:
player_links.append(link)
self.num_of_player += len(player_links)
yield from response.follow_all(player_links, self.parse_player)
print(f"***current total number of players:{self.num_of_player}")
def parse_player(self, response):
player = items.Player() # 新球員資料
soup = BeautifulSoup(response.body, 'lxml')
# 0. 球員姓名
player['name'] = soup.find(id="firstHeading").text
yield {
'title': player['name'],
}
twbaseball_spider.py
items.py:可以定義「想要擷取的欄位」
# Define here the models for your scraped items
from re import S
import scrapy
class MycrawlerItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
pass
class Player(scrapy.Item): # 球員資料
url = scrapy.Field() # 網址
name = scrapy.Field() # 球員姓名
nickname = scrapy.Field() # 綽號
birthday = scrapy.Field() # 生日
height = scrapy.Field() # 身高
weight = scrapy.Field() # 體重
position = scrapy.Field() # 守備位置
intro = scrapy.Field() # 簡介全文
experience = scrapy.Field() # 經歷
event = scrapy.Field() # 年表
performance = scrapy.Field() # 特殊事蹟
record = scrapy.Field() # 成績紀錄
links = scrapy.Field() # 外部連結
items.py
參考這邊的寫法
twbaseball_spider.py(部份)
...
class TwBaseballSpider(scrapy.Spider): # 1. 繼承scrapy.Spider
...
def parse(self, response): # 4. 頁面解析
next200 = False # pagination link processing flag
player_links = [] # 球員超連結串列
for link in response.css('#mw-pages').css("a"):
text = link.css('a::text').get() # 超連結的文字部分
if text == '先前200': # 先前200已經處理過了
continue
if text == '之後200': # 之後200 下一頁球員
if next200: # 已經處理過了
continue
else: # 尚未處理
next200 = True # 標示為已處理
self.total_page += 1
# yield response.follow(link, self.parse) # 連續循環版
print(f"^^^current total pages:{self.total_page}")
if random.randint(1,100)<=2:
player_links.append(link)
self.num_of_player += len(player_links)
yield from response.follow_all(player_links, self.parse_player)
print(f"***current total number of players:{self.num_of_player}")
...
主要的回呼函式
無限循環? 透過遞迴達成!
...
yield from response.follow_all(player_links, self.parse_player)
...
....
yield response.follow(link, self.parse) # 連續循環版
....
連往下一個超連結link,並呼叫parse()回呼函式
yield: 生成器,類似return,但可節省許多記憶體
連往player_links內所有超連結,並呼叫parse_player()回呼函式
yield from:產生許多生成器
台灣棒球維基館
twbaseball_spider.py
import random
import time
from datetime import datetime
import mycrawler.items as items # 資料欄位定義 items.xxxx
import scrapy
from bs4 import BeautifulSoup
class TwBaseballSpider(scrapy.Spider): # 1. 繼承scrapy.Spider
#### spider 設定 ####
name = "twb_reader" # 2. 為spider命名
allowed_domains = ['dils.tku.edu.tw']
start_urls = [ # 3. 起始擷取頁面串列
# 頁面:台灣球員分類
'http://twbsball.dils.tku.edu.tw/wiki/index.php/%E5%88%86%E9%A1%9E:%E5%8F%B0%E7%81%A3%E7%90%83%E5%93%A1'
]
out_file = 'twbaseball_player.csv' # 輸出檔案
custom_settings = {
'FEEDS': { out_file: { 'format': 'csv',}}
}
#### 程式自定義屬性 ####
base_url = "http://twbsball.dils.tku.edu.tw"
total_page = 0 # 分頁總數
num_of_player = 0 # 抓取的球員總數
sections = [ # data frame的column name,共9個
'簡介', # 同義詞段落'生平簡介'
'基本資料',
'經歷',
'個人年表',
'特殊事蹟',
'職棒生涯成績',
'備註',
'註釋或參考文獻',
'外部連結',
]
sid = { # 台灣棒球維基館的section id
'基本資料': '.E5.9F.BA.E6.9C.AC.E8.B3.87.E6.96.99',
'經歷': '.E7.B6.93.E6.AD.B7',
'個人年表': '.E5.80.8B.E4.BA.BA.E5.B9.B4.E8.A1.A8',
'特殊事蹟': '.E7.89.B9.E6.AE.8A.E4.BA.8B.E8.B9.9F',
'職棒生涯成績':'.E8.81.B7.E6.A3.92.E7.94.9F.E6.B6.AF.E6.88.90.E7.B8.BE',
'備註':'.E5.82.99.E8.A8.BB',
'註釋或參考文獻': '.E8.A8.BB.E9.87.8B.E6.88.96.E5.8F.83.E8.80.83.E6.96.87.E7.8D.BB',
'外部連結': '.E5.A4.96.E9.83.A8.E9.80.A3.E7.B5.90',
'結尾': 'stub'
}
intro = {
'簡介': '.E7.B0.A1.E4.BB.8B',
'生平簡介': '.E7.94.9F.E5.B9.B3.E7.B0.A1.E4.BB.8B'
}
####
def extract_introduction(self, content, key, value):
pos = str(content).find(f'id="{value}"') # 簡介 / 生平簡介
if pos == -1:
return None
else:
next_pos = str(content).find('<span class="mw-headline" id=".E5.9F.BA.E6.9C.AC.E8.B3.87.E6.96.99">基本資料</span>')
intro_str = str(content)[pos:next_pos] # 擷取 「簡介/生平簡介」 段落
intro_str = intro_str[intro_str.find(key):] # 去掉最前面未切乾淨的html碼
intro_soup = BeautifulSoup(intro_str, 'lxml') # parsing
texts = intro_soup.find_all(text=True) # 取出所有元素的文字,不含標籤屬性
# 資料格式為串列
return u"".join(t.strip() for t in texts) # 將串列元素接在一起形單一字串,u""為unicode
####使用字串.find()和取得子字串的功能,切出每個段落大致的內容
# 參數:
# content: http response回應的內容部份
# start: 開始字串(不含),要裁切內容的開始位置
# end: 結尾字串(不含), 裁切內容的結束位置
# 回傳:
# 裁切好的子字串
def get_sub_content(self, content, start, end):
start_pos = str(content).find(f'id="{start}"')
# print(f'sp:{start_pos}')
if start_pos == -1: # 找不到start字串
return None
else: # 找到 start字串
end_pos = str(content).find(f'id="{end}"') # 尋找end字串
if end_pos == -1:
return None # 找不到end字串
else:
# print(f'ep:{end_pos}')
return str(content)[start_pos: end_pos] # 回傳裁切子字串
#### 使用BeatuifulSoup的功能,取出以<dd></dd>包起來的段落內容
# 參數:
# html_str: 要分析的段落內容字串
# 回傳:
# 段落內容串列,每一個li為一個串列元素
def extract_dlist(self, html_str):
soup = BeautifulSoup(html_str, 'lxml') # parsing
li = soup.find_all('li') # 找到<li>
li_texts = [] # 空字串, 將存入所有li的文字
for item in li:
item_text = item.find_all(text=True)
if u"".join(item_text).strip() != '':
li_texts.append(u"".join(item_text).strip())
return li_texts
def parse(self, response): # 4. 頁面解析
next200 = False # pagination link processing flag
player_links = [] # 球員超連結串列
# 走訪所有超連結:(1)處理pagination (yield版本的遞迴) (2)儲存球員超連結
for link in response.css('#mw-pages').css("a"):
text = link.css('a::text').get() # 超連結的文字部分
if text == '先前200': # 先前200已經處理過了
continue
if text == '之後200': # 之後200 下一頁球員
if next200: # 已經處理過了
continue
else: # 尚未處理
next200 = True # 標示為已處理
self.total_page += 1
yield response.follow(link, self.parse) # yield版遞迴至下一頁
print(f"^^^ 已處理總頁數 ^^^:{self.total_page}")
player_links.append(link) ## 處理全部球員
#if random.randint(1, 100) <= 2:
# player_links.append(link) # link為球員連結, 加入待處理球員連結
self.num_of_player += len(player_links)
print(f'*** 目前的球員總數 ***:{self.num_of_player}')
yield from response.follow_all(player_links, self.parse_player)
def parse_player(self, response):
if response.status == 200:
soup = BeautifulSoup(response.body, 'lxml')
else:
return
# 0. 球員姓名
player_name = soup.find(id="firstHeading").text # 棒球維基館title id
# 1. url
url = response.url
# 2. content
final_result = {} # 最後擷取的全文結果
all_content = soup.find(id="bodyContent") # 取得全部HTML內文
#### 2.1 擷取「簡介」, 如果有的話
for key, value in self.intro.items():
pp = self.extract_introduction(all_content, key, value) # 取得 簡介
if pp != None:
print(f'找到{key}')
# final_result[key] = pp # 簡介
intro = pp # 簡介內容
break
else:
intro = '' # 沒有簡介
print(f'找不到{key}')
#### 2.2 擷取後面各個段落
start_str = '' # 擷取段落用的開始字串
end_str = '' # 擷取段落用的結束字串
title = '' # 目前擷取的段落名稱:對應至sid的key
next_title = '' # 下一個擷取的段落名稱:對應至sid的key
for key, value in self.sid.items(): # 遍訪sid串列,取出定位字串
if start_str == '': # 1. 設定開始字串
start_str = value # 1.1開始字串
title = key # 1.2段落名稱
continue
else:
if end_str == '': # 2. 設定結束字串
end_str = value # 2.1 結束字串
next_title = key # 2.2 下一個段落名稱
print(title)
# 使用開始字串,結束字串取得中間的html內容
sub_content_str = self.get_sub_content(all_content, start_str, end_str)
if sub_content_str == None: # 如果擷取回來沒有內容
end_str = '' # 清除結束字串,繼續處理下一個sid項目
continue
# 處理擷取回來的段落內容
li_list = self.extract_dlist(sub_content_str) # 取出當中的li
final_result[title] = ''.join(li_list) # 紀錄起來
start_str = end_str # 設定下一段的開始字串
end_str = '' # 清除結束字串
title =next_title # 設定下一段的段落名稱
now = datetime.now().strftime("%Y/%m/%d %H:%M:%S")
player_data = items.PlayerData(
name = player_name,
url = url,
datetime = now,
intro = intro,
bio = final_result['基本資料'] if '基本資料' in final_result else '',
experience = final_result['經歷'] if '經歷' in final_result else '',
event = final_result['個人年表'] if '個人年表' in final_result else '',
performance = final_result['特殊事蹟'] if '特殊事蹟' in final_result else '',
record = final_result['職棒生涯成績'] if '職棒生涯成績' in final_result else '',
notes = final_result['備註'] if '備註' in final_result else '',
refs = final_result['註釋或參考文獻']if '註釋或參考文獻' in final_result else '',
links = final_result['外部連結']if '外部連結' in final_result else '',
)
yield player_data
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
from re import S
import scrapy
class MycrawlerItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
pass
class PlayerData(scrapy.Item):
name = scrapy.Field() # 球員姓名
url = scrapy.Field() # 網址
datetime = scrapy.Field() # 擷取日期時間
intro = scrapy.Field() # 簡介全文
bio = scrapy.Field() # 基本資料
experience = scrapy.Field() # 經歷
event = scrapy.Field() # 年表
performance = scrapy.Field() # 特殊事蹟
record = scrapy.Field() # 成績紀錄
notes = scrapy.Field() # 備註
refs = scrapy.Field() # 參考文獻
links = scrapy.Field() # 外部連結
class Player(scrapy.Item):
# 球員資料
url = scrapy.Field() # 網址
name = scrapy.Field() # 球員姓名
nickname = scrapy.Field() # 綽號
birthday = scrapy.Field() # 生日
height = scrapy.Field() # 身高
weight = scrapy.Field() # 體重
position = scrapy.Field() # 守備位置
intro = scrapy.Field() # 簡介全文
experience = scrapy.Field() # 經歷
event = scrapy.Field() # 年表
performance = scrapy.Field() # 特殊事蹟
record = scrapy.Field() # 成績紀錄
links = scrapy.Field() # 外部連結
items.py
使用方法ㄧ: 透過HuggingFace Transformers
圖片來源:Transformers, PyTorch, TensorFlow
pip install -U transformers
❶ 安裝transformers套件
❷ 安裝深度學習套件(擇一)
pip install -U torch
pip install -U tensorflow
* 後續以PyTorch為例
from ckip_transformers.nlp import CkipWordSegmenter, CkipPosTagger, CkipNerChunker
## 將斷詞結果words與 part-of-speech結果pos打包在一起
def pack_ws_pos_sentece(words, pos):
assert len(words) == len(pos), f'words{len(words)}和pos{len(pos)}長度必須一樣'
result = [] # 最終結果串列
for word, p in zip(words, pos): # zip將words,pos對應位置鏈在一起
result.append(f"{word}({p})")
return "\u3000".join(result) #\u3000是全形空白字元
# Input text
text = [
"華氏80度,氣壓30-6,整天下雨。前進到奇武荖,聆聽朗誦。然後珍珠里簡和冬瓜山,晚間在珍珠里簡的禮拜有一百五十人,拔了七十六顆牙。",
"相當涼爽舒適,與阿華出去鄉間拜訪他的一位老朋友,是一位農人。我們被看見,認出來,且受排斥。遭到兩隻大黑狗攻擊,孩童吼叫,狂暴的辱罵。 我們回家,在吃過飯之後,我們比過去更勤奮的研讀各自的功課。",
"華氏84度,陰天,氣壓30-10。上午七點離開大里簡,大部分都用走的,上午時點三十分來到頭城,拔了一些牙。十一點到打馬煙,我們剛到雨就來了。禮德醫師在路上幫一個傢伙縫手指。新漆好的禮拜堂到處都是用藍色和紅色漆,全部由他們自費花了十二元。禮德醫師拍團體照。我們照顧了十四名病人,還拔了一些牙。主持聖餐禮,聆聽十七個人背誦。大家都做得很好,我們發禮物給他們。"
]
ws_driver = CkipWordSegmenter(model="bert-base") # 載入 斷詞模型
pos_driver = CkipPosTagger(model="bert-base") # 載入 POS模型
ner_driver = CkipNerChunker(model="bert-base") # 載入 實體辨識模型
print('all loaded...')
# 執行pipeline 產生結果
ws = ws_driver(text) # 斷詞
pos = pos_driver(ws) # 詞性: 注意斷詞-詞性是依序完成
ner = ner_driver(text) # 實體辨識
##列印結果
for sentence, word, p, n in zip(text, ws, pos, ner):
print(sentence)
print(pack_ws_pos_sentece(word, p))
for token in n:
print(f"({token.idx[0]}, {token.idx[1]}, '{token.ner}', '{token.word}')")
print()
華氏80度,氣壓30-6,整天下雨。前進到奇武荖,聆聽朗誦。然後珍珠里簡和冬瓜山,晚間在珍珠里簡的禮拜有一百五十人,拔了七十六顆牙。
華氏(Na) 80(Neu) 度(Nf) ,(COMMACATEGORY) 氣壓(Na) 30-6(Neu) ,(COMMACATEGORY) 整(Neqa) 天(Nf) 下雨(VA) 。(PERIODCATEGORY) 前進(VA) 到(P) 奇武荖(Nc) ,(COMMACATEGORY) 聆聽(VC) 朗誦(VC) 。(PERIODCATEGORY) 然後(D) 珍珠里簡(Na) 和(Caa) 冬瓜山(Nc) ,(COMMACATEGORY) 晚間(Nd) 在(P) 珍珠里簡(Nc) 的(DE) 禮拜(Na) 有(V_2) 一百五十(Neu) 人(Na) ,(COMMACATEGORY) 拔(VC) 了(Di) 七十六(Neu) 顆(Nf) 牙(Na) 。(PERIODCATEGORY)
(0, 5, 'QUANTITY', '華氏80度')
(8, 12, 'QUANTITY', '30-6')
(37, 40, 'LOC', '冬瓜山')
(41, 43, 'TIME', '晚間')
(52, 56, 'CARDINAL', '一百五十')
(60, 63, 'CARDINAL', '七十六')
實體辨識
11類專有名詞
7類數量詞
Part-of-Speech(POS)
61種詞性
華氏80度,氣壓30-6,整天下雨。前進到奇武荖,聆聽朗誦。然後珍珠里簡和冬瓜山,晚間在珍珠里簡的禮拜有一百五十人,拔了七十六顆牙。
華氏(Na) 80(Neu) 度(Nf) ,(COMMACATEGORY) 氣壓(Na) 30-6(Neu) ,(COMMACATEGORY) 整(Neqa) 天(Nf) 下雨(VA) 。(PERIODCATEGORY) 前進(VA) 到(P) 奇武荖(Nc) ,(COMMACATEGORY) 聆聽(VC) 朗誦(VC) 。(PERIODCATEGORY) 然後(D) 珍珠里簡(Na) 和(Caa) 冬瓜山(Nc) ,(COMMACATEGORY) 晚間(Nd) 在(P) 珍珠里簡(Nc) 的(DE) 禮拜(Na) 有(V_2) 一百五十(Neu) 人(Na) ,(COMMACATEGORY) 拔(VC) 了(Di) 七十六(Neu) 顆(Nf) 牙(Na) 。(PERIODCATEGORY)
(0, 5, 'QUANTITY', '華氏80度')
(8, 12, 'QUANTITY', '30-6')
(37, 40, 'LOC', '冬瓜山')
(41, 43, 'TIME', '晚間')
(52, 56, 'CARDINAL', '一百五十')
(60, 63, 'CARDINAL', '七十六')
只辨識出1個實體
且分類錯誤
應為GPE
3個地名,共出現四次
珍珠里簡: 不同詞性?
Na:普通名詞,Nc: 地方詞
1. 讀寫檔案
2. 資料清理
pip install pandas
pip install numpy
from bs4 import BeautifulSoup
import requests
import pandas
....
# print(final_result) # 印出擷取的資料
fr_array = [] # 準備空白表格資料
fr_array.append(final_result) # 加入一筆資料至表格
index=[i for i in range(len(fr_array))] # data frame的索引
### 建立dataframe
# 1. fr_array: 表格資料(目前只有一筆)
# 2. index=index: 前者是參數名稱,後者是第9行建立的索引串列
# 3. columns=sections: 前者是參數名稱, 後者是先前定義的欄位串列
df = pandas.DataFrame(fr_array,index=index, columns=sections)
df.to_csv('out_file.csv') # 寫出檔案(utf-8編碼)
webscraping-player.py(部份)
...
import pandas as pd
def read_file(fn):
# 日記無header,自訂header為'date','content'
df = pd.read_csv(fn,names=['date','content'])
df['content'] = df['content'].str.strip() # 去除頭尾空白字元
print(df.info())
return df
# 步驟一: 讀取馬偕日記
data_file = './data/MackayFull-202209-utf8.csv'
out_file = './data/MackayFull-202209-Output-utf8.csv'
df = read_file(data_file)
# print(df)
...
02Mackay.py(部份)
#-*-coding:UTF-8 -*-
# 讀取馬偕日記csv檔,使用CKIP斷詞、實體辨識進行處理, 並作統計
# Import Ckip Transformers module
from ckip_transformers.nlp import CkipWordSegmenter, CkipPosTagger, CkipNerChunker
import pandas as pd
def read_file(fn):
df = pd.read_csv(fn,names=['date','content']) # 日記無header,自訂header為'date','content'
df['content'] = df['content'].str.strip() # 去除日記內容頭尾的空白字元
print(df.info())
return df
# 步驟一: 讀取馬偕日記
data_file = './data/MackayFull-202209-utf8.csv'
out_file = './data/MackayFull-202209-Output-utf8.csv'
df = read_file(data_file)
# print(df)
# 步驟二: load models
ws_driver = CkipWordSegmenter(model="bert-base")
print('word segmenter loaded...')
pos_driver = CkipPosTagger(model="bert-base")
print('Pos Tagger loaded...')
ner_driver = CkipNerChunker(model="bert-base")
df['word_segment'] = ws_driver(df['content']) # 斷詞
df['part_of_speech'] = pos_driver(df['content']) # 詞性標記
df['ner_chunker'] = ner_driver(df['content']) # 實體辨識
# 合併 斷詞 於 詞性標記 的結果
sub_df = df[['word_segment','part_of_speech']] # 取出斷詞、詞性標記
word_with_pos = list()
for index in range(sub_df.shape[0]): # 每一筆
print('處理第 %d 筆' % index)
ws = sub_df['word_segment'][index] # 斷詞結果
pos = sub_df['part_of_speech'][index] # 詞性標記結果
count = 0 # 指標 for part_of_speech
words = list()
for word in ws: # 每一筆
# print(word, pos[count], sep=' ')
words.append([word, pos[count]])
count += len(word)
word_with_pos.append(words)
df['word_with_pos'] = word_with_pos
print(df['word_with_pos'])
print(df['ner_chunker'])
'''
# 版本1: 只取部分欄位
out_field = ['date','content','word_with_pos','ner_chunker']
out_df=df[out_field]
out_df.to_csv(out_file, sep='\t', encoding='utf-8')
'''
# 版本2: 輸出所有欄位
df.to_csv(out_file, sep='\t', encoding='utf-8')
02Mackay.py
有使用BERT處理文字(需另外安裝)
pip install -U transformers
➊ 用不到、不相關
➋ 資料闕漏
刪除一筆、刪除一欄、補遺
刪除欄位
➌ 資料型別錯誤
重填正確型別資料
建立資料結構
資料清理
輸出
●●●
資料庫
讀取檔案
●●●
永續報告書內容
Large Language Model
訓練資料
❶¹ 前處理
🅐 訓練集
🅑 驗證集
🅒 測試集
❷ 訓練參數
➎ 決定最終預測模型
❸ 產生預測用模型
➍ 測試模型
特徵萃取
❶² 資料集分割
遷移學習:專注於目標領域資料集收集、訓練
機器學習傳統做法
資料集1
資料集2
資料集3
任務1
任務2
任務3
兩階段: 非監督式、監督式
不同任務、不同資料集
Transfer Learning
來源領域任務
資料集S
目標領域任務
大量
資料集T
小量
預訓練unsupervised
微調supervised
GPT
PaLM
Vaswani et al., Attention is All You Need https://arxiv.org/abs/1706.03762
自注意(self-attention)機制
Encoder
Decoder
Attention Layer
Attention Layer
Attention Layer
96個Transformer解碼器
任務之一:句子 ➠ 預測下一個字
預訓練常見任務之二:克漏字
資料爬蟲
資料清理
文章內容
有哪些可能的任務?
情感分析 (Sentiment Analysis)
主題分析 (Topic Analysis/ Thematic Analysis)
新聞分類 (News Categorization)
多標籤分類(Multilabel Classification)
問答 (Question Answering)
自然語言推論 (Natural Language Inference )
自動摘要 (automatic Abstracting)
機器翻譯 (Machine Translation)
Fabien, M., Villatoro-Tello, E., Motlícek, P., & Parida, S. (2020). BertAA : BERT fine-tuning for Authorship Attribution. ICON.
❶ 準備Labeled Dataset
❷ 設計Network for Task
❸ Output Layer
內容分析的參考依據
Classification Model for Stakeholder Engagement Level
利害關係人: 任何能影響或受組織目標實現影響的團體或個人
利害關係人溝通 日月光控股
直接影響
間接影響
Freeman, R. E. (1984). Strategic management: A stakeholder approach. pp.46 Boston, MA: Pitman
效益一:增加企業商業價值
效益二:呼應股東積極主義(shareholder activism)的興起
效益三:呼應ESG投資風潮的興起
利害關係人議合: 透徹蒐集各方意見,整合至公司營運策(ESG 之Governance)
促進知情與決策、避免潛在風險、擴大與開發商機;提昇品牌價值與聲譽、促進創新
較易制定問題的解決方案、避免意見爭執、培養與利害關係人的順暢關係
迎合股東和潛在投資者於分析投資決策
2016, GRI Database
119 Energy Sectors
資料集
內容分析法
標注Engagement actions:
Track
Inform
Consult
Support
Collaborate
Partner
Engagement水準分類
Level 1: Information Strategy
Level 2: Response Strategy
Level 3: Involvement Stragegy
Stocker, F., et al., (2020). Stakeholder engagement in sustainability reporting: a classification model. Corporate Social Responsibility and Environmental Management, 27(5), 2071-2080.
據此產出 議合行動的
1) Quality(Level 1~3)
2) Focus
3) Extension
Engagement Strategy Matrix
分析報告中段落:
Stakeholder Identification & Engagement
categories, coding
reference
quality: number of actions, most cited actions at each level
quality: number of actions, most cited actions at each level
focus:
a) each company
b) the total number of actions per level
extent of the engagement:
❶ 準備Labeled Dataset
❷ 設計Network for Task
❸ Output Layer
❸ Pre-train model selection
Network Design
Thematic Analysis + Multi-label Classification
Thematic Analysis + Aspect Based Sentiment Analysis(ABSA)
Q. Zhang, R. Lu, Q. Wang, Z. Zhu and P. Liu, "Interactive Multi-Head Attention Networks for Aspect-Level Sentiment Classification," in IEEE Access, vol. 7, pp. 160017-160028, 2019
Labeled dataset: 人工標記工具