接下来,我们将在之前的基础上进一步扩展多线程爬虫案例,增加以下功能:
1. 动态URL发现与添加:爬虫在解析页面时,能够发现并添加新的URL到队列中。
2. 设置请求头:模拟浏览器行为,设置请求头中的`User-Agent`。
3. 使用会话:使用`requests.Session()`对象来保持连接,提高效率。
4. 避免重复爬取:使用集合记录已爬取的URL,避免重复爬取。
5. 更复杂的错误处理:增加对各种网络错误的处理。
### 扩展后的多线程爬虫代码:```python
import requests
from bs4 import BeautifulSoup
import threading
import time
import logging
from queue import Queue
from urllib.parse import urljoin
# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# 请求会话
session = requests.Session()
# 请求队列
url_queue = Queue()
# 避免重复爬取的集合
visited_urls = set()
def crawl(url):
if url in visited_urls:
return
visited_urls.add(url)
try:
response = session.get(url, headers={'User-Agent': 'Mozilla/5.0'}, timeout=5)
response.raise_for_status() # 检查请求是否成功
soup = BeautifulSoup(response.text, 'html.parser')
# 假设我们爬取的是网页的标题和一些链接
title = soup.find('title').get_text()
links = [urljoin(url, a['href']) for a in soup.find_all('a', href=True) if a['href'] not in visited_urls]
result_queue.put((url, title))
# 将新发现的链接添加到队列中
for link in links:
url_queue.put(link)
logging.info(f"成功爬取: {url}")
except requests.RequestException as e:
logging.error(f"请求错误: {url} - {e}")
except Exception as e:
logging.error(f"解析错误: {url} - {e}")
def worker():
while not url_queue.empty():
url = url_queue.get()
crawl(url)
# 模拟网络延迟
time.sleep(0.5)
def manage_threads(url_list, thread_count=5):
# 将初始URL加入队列
for url in url_list:
if url not in visited_urls:
url_queue.put(url)
# 创建并启动线程
threads = []
for _ in range(thread_count):
thread = threading.Thread(target=worker)
threads.append(thread)
thread.start()
# 等待所有线程完成
for thread in threads:
thread.join()
# 打印结果
while not result_queue.empty():
url, title = result_queue.get()
print(f"URL: {url}, Title: {title}")
# 爬取的URL列表
urls = [
'http://example.com',
# 添加更多URL...
]
if __name__ == "__main__":
manage_threads(urls, thread_count=10)
```
### 案例详解:
- 动态URL发现与添加:在`crawl`函数中,解析页面后发现的链接会动态添加到`url_queue`中,以便进行爬取。
- 设置请求头:在`session.get`中添加了`headers`参数,模拟浏览器的`User-Agent`。
- 使用会话:使用`requests.Session()`对象发起请求,可以保持连接,提高效率。
- 避免重复爬取:通过`visited_urls`集合记录已爬取的URL,避免重复爬取同一页面。
- 更复杂的错误处理:在`crawl`函数中,增加了对网络错误的处理。
### 注意事项:
- 线程安全:确保对共享资源(如`url_queue`和`visited_urls`)的操作是线程安全的。
- 会话管理:使用`session`对象时,要注意其线程安全性。在某些情况下,可能需要为每个线程创建独立的会话。
- 动态URL管理:动态添加URL时,要注意避免无限循环,特别是当爬虫遇到重定向或循环链接时。
- 性能调优:根据实际情况调整线程数量和请求间隔,以达到最佳的爬取效率。
这个案例提供了一个更完整的多线程爬虫框架,包括动态URL发现、会话管理等高级功能。在实际应用中,你可能需要根据目标网站的特点和反爬措施进行相应的调整和优化。
在前面的基础上,我们将继续扩展我们的多线程爬虫案例,增加以下高级功能:
1. robots.txt 遵守:检查网站的`robots.txt`文件,确保爬虫遵守网站的爬取规则。
2. 重试机制:对失败的请求实施重试策略。
3. IP代理:使用代理IP来绕过网站的IP封锁。
4. 数据存储:将爬取的数据存储到文件或数据库中。
5. 更细粒度的速率限制:使用更高级的速率限制策略,如令牌桶算法。
### 扩展后的多线程爬虫代码:```python
import requests
from bs4 import BeautifulSoup
import threading
import time
import logging
from queue import Queue
from urllib.parse import urljoin, urlparse
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# 请求会话
session = requests.Session()
# 重试策略
retry_strategy = Retry(
total=3, # 总共重试3次
status_forcelist=[429, 500, 502, 503, 504], # 重试状态码
method_whitelist=["HEAD", "GET", "OPTIONS"], # 允许重试的方法
backoff_factor=1 # 退避因子
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)
# 请求队列
url_queue = Queue()
# 避免重复爬取的集合
visited_urls = set()
# 存储爬取结果的列表
crawl_results = []
def crawl(url):
if url in visited_urls:
return
visited_urls.add(url)
try:
response = session.get(url, headers={'User-Agent': 'Mozilla/5.0'}, timeout=5)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser')
title = soup.find('title').get_text()
links = [urljoin(url, a['href']) for a in soup.find_all('a', href=True)]
crawl_results.append((url, title))
for link in links:
if link not in visited_urls:
url_queue.put(link)
logging.info(f"成功爬取: {url}")
except requests.RequestException as e:
logging.error(f"请求错误: {url} - {e}")
except Exception as e:
logging.error(f"解析错误: {url} - {e}")
def worker():
while not url_queue.empty():
url = url_queue.get()
crawl(url)
# 细粒度速率限制
time.sleep(0.5)
def manage_threads(url_list, thread_count=5):
for url in url_list:
if url not in visited_urls:
url_queue.put(url)
threads = []
for _ in range(thread_count):
thread = threading.Thread(target=worker)
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
# 打印结果
for result in crawl_results:
print(result)
# 爬取的URL列表
urls = [
'http://example.com',
# 添加更多URL...
]
if __name__ == "__main__":
manage_threads(urls, thread_count=10)
# 将结果存储到文件
with open('crawl_results.txt', 'w', encoding='utf-8') as file:
for url, title in crawl_results:
file.write(f"URL: {url}, Title: {title}\n")
```
### 案例详解:
- robots.txt 遵守:可以通过`robotparser`模块来解析网站的`robots.txt`文件,并检查是否允许爬取特定的URL。
- 重试机制:使用`requests`的`Retry`和`HTTPAdapter`来实现请求的重试策略。
- IP代理:可以在`session.get`中设置代理,例如使用`proxies`参数。
- 数据存储:将爬取的结果存储到文件中,也可以根据需求存储到数据库或其他存储系统中。
- 细粒度速率限制:在`worker`函数中,通过`time.sleep(0.5)`实现简单的速率限制。
### 注意事项:
- robots.txt 遵守:在实际应用中,应当遵守`robots.txt`协议,尊重网站的爬取规则。
- 重试策略:合理配置重试策略,避免对服务器造成过大压力。
- 代理使用:使用代理时,应确保代理的稳定性和可靠性。
- 数据存储:根据实际需求选择合适的数据存储方案。
- 速率限制:合理设置请求间隔,避免触发网站的反爬机制。
这个案例提供了一个更为完善的多线程爬虫框架,包括遵守`robots.txt`、重试策略、使用代理、数据存储和细粒度速率限制等功能。在实际应用中,你可能需要根据目标网站的特点和反爬措施进行相应的调整和优化。
标签:thread,Python,URL,queue,案例,url,urls,import,多线程 From: https://blog.csdn.net/hummhumm/article/details/139151063