內容存取方法論
適用於新聞與研究的倫理且合法的受限網路內容存取途徑。
存取層級(從最優先到最不建議)
┌─────────────────────────────────────────────────────────────────┐
│ 內容存取決策層級 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 完全合法(永遠優先嘗試) │
│ ├─ 圖書館資料庫(PressReader, ProQuest, JSTOR) │
│ ├─ 開放取用工具(Unpaywall, CORE, PubMed Central) │
│ ├─ 直接聯繫作者 │
│ └─ 館際互借 │
│ │
│ 2. 合法(瀏覽器功能) │
│ ├─ 閱讀模式(Safari, Firefox, Edge) │
│ ├─ Wayback Machine 檔案庫 │
│ └─ Google Scholar「所有版本」 │
│ │
│ 3. 灰色地帶(謹慎使用) │
│ ├─ Archive.is 用於個別文章 │
│ ├─ 停用 JavaScript(可能破壞功能) │
│ └─ VPN 用於地理封鎖內容 │
│ │
│ 4. 不建議 │
│ ├─ 憑證共享 │
│ ├─ 系統性爬取 │
│ └─ 規避內容的商業使用 │
│ │
└─────────────────────────────────────────────────────────────────┘
學術論文的開放取用工具
Unpaywall 瀏覽器擴充功能
Unpaywall 尋找超過 2000 萬篇學術論文的免費、合法副本。
# Unpaywall API(免費,需提供電子郵件以識別身分)
import requests
def find_open_access(doi: str, email: str) -> dict:
"""使用 Unpaywall API 尋找論文的開放取用版本。
Args:
doi: 數位物件識別碼(例如 "10.1038/nature12373")
email: 用於 API 識別身分的電子郵件
Returns:
如果可用,回傳包含最佳開放取用 URL 的字典
"""
url = f"https://api.unpaywall.org/v2/{doi}?email={email}"
response = requests.get(url, timeout=30)
if response.status_code != 200:
return {'error': f'狀態碼 {response.status_code}'}
data = response.json()
if data.get('is_oa'):
best_location = data.get('best_oa_location', {})
return {
'is_open_access': True,
'oa_url': best_location.get('url_for_pdf') or best_location.get('url'),
'oa_status': data.get('oa_status'), # gold, green, bronze, hybrid
'host_type': best_location.get('host_type'), # publisher, repository
'version': best_location.get('version') # publishedVersion, acceptedVersion
}
return {
'is_open_access': False,
'title': data.get('title'),
'journal': data.get('journal_name')
}
# 使用範例
result = find_open_access("10.1038/nature12373", "[email protected]")
if result.get('is_open_access'):
print(f"免費 PDF 位於: {result['oa_url']}")
CORE API(2.95 億篇論文)
# CORE API - 需要從 https://core.ac.uk/ 取得的免費 API 金鑰
import requests
class CORESearch:
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = "https://api.core.ac.uk/v3"
def search(self, query: str, limit: int = 10) -> list:
"""搜尋 CORE 資料庫中的開放取用論文。"""
headers = {'Authorization': f'Bearer {self.api_key}'}
params = {
'q': query,
'limit': limit
}
response = requests.get(
f"{self.base_url}/search/works",
headers=headers,
params=params,
timeout=30
)
if response.status_code != 200:
return []
data = response.json()
results = []
for item in data.get('results', []):
results.append({
'title': item.get('title'),
'authors': [a.get('name') for a in item.get('authors', [])],
'year': item.get('yearPublished'),
'doi': item.get('doi'),
'download_url': item.get('downloadUrl'),
'abstract': item.get('abstract', '')[:500]
})
return results
def get_by_doi(self, doi: str) -> dict:
"""根據 DOI 取得論文。"""
headers = {'Authorization': f'Bearer {self.api_key}'}
response = requests.get(
f"{self.base_url}/works/{doi}",
headers=headers,
timeout=30
)
return response.json() if response.status_code == 200 else {}
Semantic Scholar API(2.14 億篇論文)
# Semantic Scholar API - 免費,基本使用無需金鑰
import requests
def search_semantic_scholar(query: str, limit: int = 10) -> list:
"""搜尋 Semantic Scholar 中具有開放取用連結的論文。"""
url = "https://api.semanticscholar.org/graph/v1/paper/search"
params = {
'query': query,
'limit': limit,
'fields': 'title,authors,year,abstract,openAccessPdf,citationCount'
}
response = requests.get(url, params=params, timeout=30)
if response.status_code != 200:
return []
results = []
for paper in response.json().get('data', []):
oa_pdf = paper.get('openAccessPdf', {})
results.append({
'title': paper.get('title'),
'authors': [a.get('name') for a in paper.get('authors', [])],
'year': paper.get('year'),
'citations': paper.get('citationCount', 0),
'open_access_url': oa_pdf.get('url') if oa_pdf else None,
'abstract': paper.get('abstract', '')[:500] if paper.get('abstract') else ''
})
return results
def get_paper_by_doi(doi: str) -> dict:
"""根據 DOI 取得論文詳細資訊。"""
url = f"https://api.semanticscholar.org/graph/v1/paper/DOI:{doi}"
params = {
'fields': 'title,authors,year,abstract,openAccessPdf,references,citations'
}
response = requests.get(url, params=params, timeout=30)
return response.json() if response.status_code == 200 else {}
瀏覽器閱讀模式 vs 軟性付費牆
啟動閱讀模式
// 觸發 Firefox 風格閱讀模式的書籤小工具
// 適用於某些在封鎖前載入內容的軟性付費牆
javascript:(function(){
// 嘗試提取文章內容
var article = document.querySelector('article') ||
document.querySelector('[role="main"]') ||
document.querySelector('.article-body') ||
document.querySelector('.post-content');
if (article) {
// 移除付費牆覆蓋層
document.querySelectorAll('[class*="paywall"], [class*="subscribe"], [id*="paywall"]')
.forEach(el => el.remove());
// 移除固定位置的覆蓋層
document.querySelectorAll('*').forEach(el => {
var style = getComputedStyle(el);
if (style.position === 'fixed' && style.zIndex > 100) {
el.remove();
}
});
// 重新啟用捲動
document.body.style.overflow = 'auto';
document.documentElement.style.overflow = 'auto';
console.log('覆蓋層已移除。內容可能現在可見。');
}
})();
各瀏覽器的閱讀模式
| 瀏覽器 | 如何啟動 | 效果 |
|---|---|---|
| Safari | 點擊網址列中的閱讀器圖示 | 對軟性付費牆效果佳 |
| Firefox | 點擊閱讀檢視圖示(或按 F9) | 效果佳 |
| Edge | 點沈沉浸式閱讀器圖示 | 最佳 |
| Chrome | 需啟用標記:chrome://flags/#enable-reader-mode | 中等 |
圖書館資料庫存取
以程式方式檢查圖書館存取權限
# 大多數圖書館資料庫需要驗證
# 此範例展示如何建構圖書館 API 存取
class LibraryAccess:
"""圖書館資料庫的存取模式。"""
# 常見的圖書館資料庫端點
DATABASES = {
'pressreader': {
'base': 'https://www.pressreader.com',
'auth': 'library_card',
'content': '超過 7000 種報紙/雜誌'
},
'proquest': {
'base': 'https://www.proquest.com',
'auth': 'institutional',
'content': '新聞、論文、文件'
},
'jstor': {
'base': 'https://www.jstor.org',
'auth': 'institutional',
'content': '學術期刊、書籍'
},
'nexis_uni': {
'base': 'https://www.nexisuni.com',
'auth': 'institutional',
'content': '法律、新聞、商業'
}
}
@staticmethod
def get_pressreader_access_methods():
"""透過圖書館存取 PressReader 的方法。"""
return {
'in_library': '連接到圖書館 WiFi,造訪 pressreader.com',
'remote': '使用圖書館借書證憑證登入',
'app': '下載 PressReader 應用程式,連結圖書館借書證',
'note': '每次登入通常可存取 30-48 小時'
}
# 館際互借 (ILL) 工作流程
def request_via_ill(paper_info: dict, library_email: str) -> str:
"""產生館際互借請求。
ILL 透過大多數圖書館免費提供,幾乎可以取得任何論文。
處理時間:通常 3-7 天。
"""
request = f"""
館際互借請求
標題: {paper_info.get('title')}
作者: {paper_info.get('authors')}
期刊: {paper_info.get('journal')}
年份: {paper_info.get('year')}
DOI: {paper_info.get('doi')}
卷期: {paper_info.get('volume')}/{paper_info.get('issue')}
頁碼: {paper_info.get('pages')}
請求者: {library_email}
"""
return request.strip()
使用 VPN 存取地理封鎖內容
何時使用 VPN 是適當的
## 記者/研究人員的合法 VPN 使用案例
### 適當:
- 存取特定地區的新聞來源
- 研究所謂內容在其他國家的呈現方式
- 繞過政府審查(在某些情況下)
- 保護來源通訊
- 驗證針對特定地理位置的內容
### 不當:
- 規避合法存取控制
- 存取您合約禁止檢視的內容
- 規避針對您帳戶的封鎖或禁制
VPN 服務比較
| 服務 | 最適合 | 隱私 | 速度 | 價格 |
|---|---|---|---|---|
| ExpressVPN | 規避審查 | 極佳 | 快 | $$$ |
| NordVPN | 一般使用 | 極佳 | 快 | $$ |
| Surfshark | 預算、無限裝置 | 佳 | 佳 | $ |
| ProtonVPN | 注重隱私 | 極佳 | 中等 | $$ |
| Tor Browser | 最大匿名性 | 極佳 | 慢 | 免費 |
檢查地理限制狀態
import requests
def check_geo_access(url: str, regions: list = None) -> dict:
"""檢查 URL 是否可從不同地區存取。
注意:實際測試時需要 VPN/代理服務。
此函數僅展示概念。
"""
regions = regions or ['US', 'UK', 'EU', 'JP', 'AU']
results = {}
# 直接存取測試
try:
response = requests.get(url, timeout=10)
results['direct'] = {
'accessible': response.status_code == 200,
'status_code': response.status_code
}
except Exception as e:
results['direct'] = {'accessible': False, 'error': str(e)}
# 需要 VPN/代理整合以進行區域測試
# results[region] = test_through_proxy(url, region)
return results
基於檔案庫的存取
使用 Archive.today 存取付費牆文章
import requests
from urllib.parse import quote
def get_archived_article(url: str) -> str:
"""嘗試從 Archive.today 取得文章。
Archive.today 經常擷取完整的文章內容,
因為它會渲染 JavaScript 並捕獲結果。
法律狀態因管轄區而異 - 請用於研究目的。
"""
# 檢查現有檔案
search_url = f"https://archive.today/{quote(url, safe='')}"
try:
response = requests.get(search_url, timeout=30, allow_redirects=True)
if response.status_code == 200 and 'archive.today' in response.url:
return response.url
# 無現有檔案 - 可要求建立一個
# 注意:這可能違反服務條款,請負責任地使用
return None
except Exception:
return None
Wayback Machine 用於歷史存取
def get_wayback_article(url: str) -> str:
"""從 Wayback Machine 取得文章。
100% 合法 - Internet Archive 是公認的圖書館。
可能包含文章較舊的版本(在付費牆建立之前)。
"""
# 檢查可用性
api_url = f"http://archive.org/wayback/available?url={url}"
try:
response = requests.get(api_url, timeout=10)
data = response.json()
snapshot = data.get('archived_snapshots', {}).get('closest', {})
if snapshot.get('available'):
return snapshot['url']
return None
except Exception:
return None
Google Scholar 策略
尋找免費版本
def find_free_via_scholar(title: str) -> list:
"""搜尋策略,用於尋找免費的論文版本。
Google Scholar 通常連結到:
- 作者個人網站上的副本
- 機構典藏版本
- ResearchGate/Academia
…
[因翻譯而截斷]