定向爬虫--爬取中国大学排名(bs4和requests库)

该博客介绍了用Python实现大学排名定向爬虫的方法。输入大学排名URL链接,输出排名、大学名称和总分信息。程序分三步:获取网页内容、提取信息到二维数据结构、展示输出结果。还提到爬取前查看robots协议,以及解决中文对齐问题的方法。

预览:

功能描述:

输入:大学排名URL链接(这里网址为:http://www.zuihaodaxue.cn/zuihaodaxuepaiming2016.html

输出:大学排名信息的屏幕输出(排名,大学名称,总分)

定向爬虫:仅对输入的URL进行爬取,不扩展爬取

程序的结构设计:

步骤1:从网络上获取大学排名网页内容

步骤2:提取网页内容中信息到合适的数据结构(从上图可以看出,存储为二维数据结构)

步骤3:利用数据结构展示并输出结果

代码如下:

import requests
from bs4 import BeautifulSoup
import bs4
def getHTMLText(url):
    try:
        r = requests.get(url,timeout = 30)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return ""

def fillUnivList(ulist,html):
    soup = BeautifulSoup(html,"html.parser")
    for tr in soup.find('tbody').children:
        if isinstance(tr,bs4.element.Tag):
            tds = tr('td')#等价于tds = tr.find_all('td')
            ulist.append([tds[0].string,tds[1].string,tds[3].string])
def printUnivList(ulist,num):
    #代码优化
    tplt = "{0:^8}\t{1:{3}^10}\t{2:<10}"
    #print("{:<10}\t{:<14}\t{:<10}".format("排名","学校名称","总分"))
    print(tplt.format("排名","学校名称","总分",chr(12288)))
    for i in range(num):
        u=ulist[i]
        print(tplt.format(u[0],u[1],u[2],chr(12288)))
    #print("Suc"+str(num))

def main():
    uinfo = []
    url = "http://www.zuihaodaxue.cn/zuihaodaxuepaiming2016.html"
    html = getHTMLText(url)
    fillUnivList(uinfo,html)
    printUnivList(uinfo,20) #只打印20个大学的信息
main()

解释一波:

爬取前首先要了解一下网页的源代码:

我们观察到,所有的大学信息,都封装在一个表格中,表格的标签叫tbody。在tbody中,每一个大学的信息又被封装一个标签中,这个标签叫tr。每个tr中的信息,又被td所包围。所以首先找到tbody,找到所有大学的信息;在找到tr,找到每个大学信息;在找到td,将我们所需要的信息,添加到二维列表中。这样就完成了信息提取工作。

当然了,爬取一个网站或者网页之前,首先要看robots协议:

没有,是可以进行爬取的。

爬取和提取工作做完之后,就是打印输出工作了,这里用到了python中的format函数。

输出的时候可能会遇到中文对齐的问题,出现的主要原因是:当中文字符宽度不够时,采用西文字符填充,但是中西文字符占用宽度不同。

解决方法:采用中文字符的空格填充chr(12288)。具体操作可以看下上面的代码。

<think>我们选择爬取全书网(quanshuwang.com)的小说信息。全书网是一个小说阅读网站,有大量的小说资源。由于全书网可能有反爬机制,我们将使用requests,配合伪造请求头、解析HTML(使用BeautifulSoup)等技术。目标:爬取小说列表(包括小说名、作者、简介、章节链接等),并进一步爬取小说某一章节的内容(以一章为例)。步骤:1.分析全书网页面结构,确定要爬取的页面URL。2.发送请求,获取网页内容。注意设置请求头模拟浏览器访问。3.解析页面,提取所需信息(先抓大:小说列表;再抓小:每一本小说的具体信息)。4.对动态加载部分,我们观察全书网,发现其小说列表是静态页面,但章节列表可能也是静态的,不需要用到Selenium。5.异常处理:网络请求异常、解析异常等。6.由于是示例,我们只爬取第一页的小说列表,并选取第一本小说爬取其第一章内容。注意:全书网可能会更新,因此以下代码可能需要根据实际网页结构调整。我们选择爬取全书网的“玄幻小说”类别第一页:http://www.quanshuwang.com/list/1_1.html对于每一本小说,进入其详情页(例如:http://www.quanshuwang.com/book/0/1),然后找到第一章的链接,再进入章节爬取内容。另外,注意遵守robots.txt,全书网的robots.txt为:http://www.quanshuwang.com/robots.txt我们检查一下:User-agent:*Disallow:/?这个禁止所有爬虫?但实际上我们可能用于学习目的,且只爬取少量数据。但为了合法,我们最好设置延时,避免过于频繁访问。考虑到可能存在的反爬,我们使用随机User-AgentReferer,并设置请求间隔。由于要求使用Python3.8,我们代码将兼容3.8。我们将采用以下步骤:1.获取小说列表页的HTML。2.解析列表页,得到每本小说的链接、书名、作者等信息。3.进入小说详情页,解析出章节列表(只取第一章)。4.进入第一章的页面,解析出正文内容。注意:全书网的小说详情页中,章节列表在一个<divid="mulu">中,但是实际上我们观察发现,章节列表在另一个页面(mulu.html)中,所以我们需要调整策略。经过观察:在小说详情页(如:http://www.quanshuwang.com/book/11/11615)中,有一个“阅读”按钮,点击会跳转到章节列表页(如:http://www.quanshuwang.com/book/11/11615/),所以我们直接访问这个URL。但注意:这个章节列表页的URL详情页的URL后面加了一个斜杠。所以我们构造URL时注意。另外,在章节列表页(http://www.quanshuwang.com/book/11/11615/)中,章节列表在<divclass="clearfixdirconone">下的<a>标签中。具体步骤:1.从列表页解析出小说详情页链接(不带斜杠),然后我们手动在链接后加上斜杠,得到章节列表页的URL。2.访问章节列表页,解析第一章链接。3.访问第一章链接,解析内容。由于网络请求可能有延迟,我们使用time.sleep()设置间隔。另外,注意异常处理。我们将使用fake_useragent来随机生成User-Agent。安装依赖(如果需要):pipinstallrequestsbeautifulsoup4fake_useragent由于是示例,我们只爬取第一页的第一本小说的第一章。如果遇到验证码,可能需要更换IP或其他反反爬措施,但这里我们只做基本处理。代码结构:1.导入模块2.定义函数获取网页内容(带请求头,支持重试)3.解析列表页4.解析小说详情页(实际上是章节列表页)5.解析章节内容页6.主函数注意:全书网的章节内容页中,正文部分在<divid="content">中,但注意其中有很多广告,我们需要提取id为"content"的div内的文本。由于网站可能会有变化,这里以实际访问时的结构为准。下面编写完整代码:</think> # 全书网小说爬虫项目 下面是一个爬取全书网(quanshuwang.com)小说信息的完整爬虫代码,采用多线程机制、异常处理伪装请求头等技术。 ```python import requests from bs4 import BeautifulSoup import threading import time import random import re from queue import Queue import csv from datetime import datetime from fake_useragent import UserAgent # 伪装请求头生成器 ua = UserAgent() # 爬虫配置 BASE_URL = "http://www.quanshuwang.com" CATEGORY_URL = "http://www.quanshuwang.com/list/1_1.html" # 玄幻小说类别 MAX_THREADS = 5 # 最大线程数 REQUEST_DELAY = (1, 3) # 随机请求延迟范围() PAGE_LIMIT = 3 # 爬取列表页数限制 BOOK_LIMIT = 6 # 每页爬取书目限制 OUTPUT_FILE = f"quanshu_novels_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" class QuanshuSpider: def __init__(self): self.book_queue = Queue() self.book_list = [] self.headers = {'User-Agent': ua.random} self.lock = threading.Lock() self.stop_event = threading.Event() def get_page(self, url, max_retries=3): """获取网页内容,带异常处理重试机制""" for _ in range(max_retries): try: time.sleep(random.uniform(*REQUEST_DELAY)) headers = {'User-Agent': ua.random} response = requests.get(url, headers=headers, timeout=10) response.encoding = 'gbk' # 全书网使用GBK编码 if response.status_code == 200: return response.text elif response.status_code == 403: print(f"访问被拒绝:{url},尝试更换代理或User-Agent") except (requests.exceptions.RequestException, UnicodeDecodeError) as e: print(f"请求错误:{e}") time.sleep(5) return None def parse_category(self, url): """解析类别列表页""" html = self.get_page(url) if not html: return [] soup = BeautifulSoup(html, 'lxml') books = [] # 获取书籍列表 book_items = soup.select('div.l.book_list > div.book_list > ul > li') for item in book_items[:BOOK_LIMIT]: try: book_url = item.select_one('a.bookimg')['href'] title = item.select_one('a.bookimg')['title'] author = item.select_one('span.author').text.split(':')[1] status = item.select_one('span.status').text.split(':')[1] books.append({ 'title': title, 'url': book_url, 'author': author, 'status': status }) except Exception as e: print(f"解析书籍列表出错: {e}") # 获取下一页链接 next_page = soup.select_one('a.next') return books, next_page['href'] if next_page else None def parse_book_detail(self, book_info): """解析书籍详情页""" html = self.get_page(book_info['url']) if not html: return book_info soup = BeautifulSoup(html, 'lxml') try: # 书籍简介 book_info['intro'] = soup.select_one('div.bookIntro > p.intro').text.strip() # 书籍封面 book_info['cover'] = soup.select_one('div.book_l > a.bookimg > img')['src'] # 章节列表URL(特殊处理) chapter_url = soup.select_one('a.read')['href'] if chapter_url.startswith('/'): chapter_url = BASE_URL + chapter_url book_info['chapter_url'] = chapter_url # 获取部分章节 book_info['chapters'] = self.parse_chapter_list(chapter_url, sample=True) except Exception as e: print(f"解析书籍详情出错 {book_info['title']}: {e}") return book_info def parse_chapter_list(self, chapter_url, sample=False): """解析章节列表页""" html = self.get_page(chapter_url) if not html: return [] soup = BeautifulSoup(html, 'lxml') chapters = [] try: # 获取目录区域 chapter_items = soup.select('div.clearfix.dirconone > li > a') # 只获取前5章作为样例 for item in chapter_items[:5] if sample else chapter_items: chapter_title = item.text.strip() chapter_url = item['href'] if chapter_url.startswith('/'): chapter_url = BASE_URL + chapter_url chapters.append({ 'title': chapter_title, 'url': chapter_url }) except Exception as e: print(f"解析章节列表出错 {chapter_url}: {e}") return chapters def parse_chapter_content(self, chapter_url): """解析章节内容""" html = self.get_page(chapter_url) if not html: return "" # 使用正则表达式提取内容 content = "" try: # 匹配章节正文内容 content_match = re.search(r'<div class="mainContenr" id="content">(.*?)</div>', html, re.S) if content_match: content = content_match.group(1) # 清理内容 content = re.sub(r'<script.*?</script>', '', content, flags=re.I|re.S) content = re.sub(r'<!--.*?-->', '', content, flags=re.S) content = re.sub(r'\s+', '', content) content = content.replace('<br/>', '\n').replace('<br />', '\n') content = BeautifulSoup(content, 'lxml').text.strip() except Exception as e: print(f"解析章节内容出错 {chapter_url}: {e}") return content[:500] + "..." if content else "获取失败" # 只取部分内容 def worker(self): """工作线程函数""" while not self.stop_event.is_set(): try: book_info = self.book_queue.get(timeout=10) # 解析书籍详情 book_info = self.parse_book_detail(book_info) # 解析第一章内容 if book_info.get('chapters'): book_info['sample_content'] = self.parse_chapter_content(book_info['chapters'][0]['url']) else: book_info['sample_content'] = "无章节信息" # 添加书籍到列表 with self.lock: self.book_list.append(book_info) print(f"已抓取: {book_info['title']} ({len(book_info['chapters'])}章)") self.book_queue.task_done() except Exception as e: if not self.stop_event.is_set(): print(f"线程出错: {e}") def start_crawl(self): """启动爬虫""" print("启动全书网小说爬虫...") start_time = time.time() # 1. 抓取小说列表 current_url = CATEGORY_URL page_count = 0 # 爬取指定页数的小说列表 while current_url and page_count < PAGE_LIMIT: page_count += 1 books, next_page = self.parse_category(current_url) if next_page: current_url = BASE_URL + next_page if next_page.startswith('/') else next_page else: break for book in books: self.book_queue.put(book) # 2. 启动工作线程处理书籍详情 threads = [] for _ in range(MAX_THREADS): t = threading.Thread(target=self.worker) t.daemon = True t.start() threads.append(t) # 3. 等待任务完成 self.book_queue.join() self.stop_event.set() # 4. 保存结果到CSV self.save_results() print(f"爬取完成! 共抓取 {len(self.book_list)} 本书籍, 耗时: {time.time()-start_time:.2f}秒") print(f"结果已保存到: {OUTPUT_FILE}") def save_results(self): """保存结果到CSV文件""" if not self.book_list: return with open(OUTPUT_FILE, 'w', newline='', encoding='utf-8-sig') as f: writer = csv.writer(f) writer.writerow(['书名', '作者', '状态', '简介', '封面URL', '章节数', '第一章标题', '样本内容']) for book in self.book_list: first_chapter = book['chapters'][0]['title'] if book.get('chapters') else '无' writer.writerow([ book.get('title', ''), book.get('author', ''), book.get('status', ''), book.get('intro', '')[:100] + '...' if book.get('intro') else '', book.get('cover', ''), len(book['chapters']) if book.get('chapters') else 0, first_chapter, book.get('sample_content', '')[:50] + '...' ]) if __name__ == '__main__': spider = QuanshuSpider() spider.start_crawl() ``` ## 代码解释 ### 1. 爬虫架构设计 - **多线程机制**:使用`threading``Queue`实现生产者-消费者模型 - **先抓大再抓小策略**: 1. 首先抓取小说列表("大") 2. 然后抓取每本书的详情("中") 3. 最后抓取章节内容("小") ### 2. 主要技术实现 #### 反反爬措施 - **伪造请求头**:使用`fake_useragent`随机生成User-Agent - **请求延迟**:随机延迟1-3秒防止频繁请求 - **重试机制**:3次失败重试机制 - **编码处理**:正确设置GBK编码处理全书网 #### 页面解析技术 - **BeautifulSoup**:用于解析HTML结构提取数据 - **CSS选择器**:通过CSS选择器提取书籍信息章节列表 - **正则表达式**:通过正则提取难以用BS4解析的内容 #### 多线程处理 - 主线程抓取书籍列表 - 工作线程并行处理书籍详情章节内容 - 线程同步使用`Lock`确保数据安全 #### 数据存储 - 结果保存为CSV文件,含时间戳防止覆盖 - 只存储关键信息,简介内容做了截断处理 ### 3. 工作流程 1. 爬取小说类别页(玄幻小说) 2. 解析每本书的基本信息(标题、作者、状态) 3. 对每本书: - 获取书籍详情页,提取简介封面 - 获取章节列表页,提取前5章信息 - 获取第一章前500字作为样本内容 4. 所有数据存储到CSV文件 ## 运行说明 1. 需要安装的包: ```bash pip install requests beautifulsoup4 fake-useragent ``` 2. 配置参数: - `MAX_THREADS`:最大线程数(不要设置太高) - `PAGE_LIMIT`:要爬取的列表页数 - `BOOK_LIMIT`:每页爬取的最大书目数 3. 输出: - CSV文件包含书名、作者、状态、简介、封面URL、章节数等信息 4. 注意事项: - 网站结构可能变更,需定期更新CSS选择器 - 尊重robots.txt,设置合理的爬取速率 - 代码只用于学习目的,不可频繁请求
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值