爬虫一般指从网络资源的抓取,通过Python语言的脚本特性,配置字符的处理非常灵活,Python有丰富的网络抓取模块,因而两者经常联系在一起Python就被叫作爬虫。爬虫可以抓取某个网站或者某个应用的内容提取有用的价值信息。有时还可以模拟用户在浏
览器或app应用上的操作行为,从而实现程序自动化。
1、爬虫架构
爬虫架构通常由5个部分组成,分别是调度器、URL管理器、网页下载器、网页解析器、应用程序。
- 调度器:相当电脑的CPU,主要负责调度URL管理器、下载器、解析器之间的协调工作。
- URL管理器:包括待爬取的URL地址和已爬取的URL地址,防止重复抓取URL和循环抓取URL。实现URL管理器通常有三种方式,通过内存、数据库、缓存方式实现。
- 网页下载器:通过传入一个URL地址来下载网页,将网页转换成一个字符串,网页下载器有urllib2(Python官方基础模块)包括需要登录、代理、和cookie,requests(第三方包)。
- 网页解析器:用于某个网页字符串进行解析,可以按照我们的要求来提取出有用的信息,也可以根据DOM树的解析方式来解析。常用的解析器有html.parser(python自带的)、beautifulsoup(也可以使用python自带的html.parser进行解析,也可以使用lxml进行解析,相对于其他几种来说要强大一些)、lxml(可以解析 xml 和 HTML),通过html.parser 和 beautifulsoup 以及 lxml 都是以DOM 树的方式进行解析。
- 应用程序:用于从网页中提取的有用数据组成的一个应用。
2、爬虫实现
2.1、Url管理器(基于内存)
class UrlManager():"""url 管理器,用来装载网址所有地址"""def __init__(self):# 新url 集合self.new_urls = set()# 旧url 集合self.old_urls = set()def add_new_url(self, url):"""添加新的url到集合:param url: url:return:"""if url is None or len(url) == 0:returnif url in self.new_urls or url in self.old_urls:returnself.new_urls.add(url)def add_new_urls(self, urls):"""批量添加urls:param urls: url:return:"""if urls is None or len(urls) == 0:returnfor url in urls:self.add_new_url(url)def get_url(self):"""获取url: 从new_urls集合获取url,放入到old_urls:return:"""if self.has_new_url():url = self.new_urls.pop()self.old_urls.add(url)return urlelse:return Nonedef has_new_url(self):"""判断是否有新的url:return:"""return len(self.new_urls) > 0if __name__ == '__main__':url_manager = UrlManager()url_manager.add_new_url('url1')url_manager.add_new_urls(['url1','url2'])print(url_manager.new_urls, url_manager.old_urls)print("#" * 30)new_url = url_manager.get_url()print(url_manager.new_urls, url_manager.old_urls)print("#" * 30)new_url = url_manager.get_url()print(url_manager.new_urls, url_manager.old_urls)print("#" * 30)print(url_manager.has_new_url())
2.2 网页解析
import re
from utils import url_manager
import requests
from bs4 import BeautifulSoupdef download_all_urls(root_url):"""爬取根网址所有页面的url:param root_url: 根网址地址:return:"""urls = url_manager.UrlManager()urls.add_new_url(root_url)fout = open("craw_all_pages.txt", "w", encoding="utf-8")while urls.has_new_url():curr_url = urls.get_url()r = requests.get(curr_url, timeout=5)r.encoding = "utf-8"if r.status_code != 200:print("error, return status_code is not 200", curr_url)continuesoup = BeautifulSoup(r.text, "html.parser", from_encoding="utf-8")title = soup.title.stringfout.write("%s\t%s\n" % (curr_url, title))fout.flush()print("success: %s, %s, %d" % (curr_url, title, len(urls.old_urls)))links = soup.find_all("a")for link in links:href = link.get("href")if href is None:continue#模式匹配pattern = r'^https://www.runoob.com/python/\s+.html$'if re.match(pattern, href):urls.add_new_url(href)fout.close()if __name__ == '__main__':#定义根网址urlroot_url = "https://www.runoob.com/python/python-tutorial.html"download_all_urls(root_url)
3、经典案例
例如:读取某网站Top250电影。
import pprint
import json
import requests
from bs4 import BeautifulSoup
import pandas as pd"""
# requests 请求网站地址
# beautifulsoup4 解析网址的elements(div,class,<a>,<img> id)等
# pandas 将数据写入到excel
# 其他额外安装包openpyxl
"""# 构造分页数字列表(步长/pageSize/step=25)
page_indexs = range(0, 250, 25)
list(page_indexs)def download_all_htmls(root_url, headers):"""下载所有页面的html内容,用于后续分析:return:"""htmls = []for idx in page_indexs:url = "%s?start=%d&filter=" % (root_url, idx)# url = f"https://movie.douban.com/top250?start={idx}&filter="print("craw html:", url)r = requests.get(url, timeout=5, headers=headers)if r.status_code != 200:raise Exception("error")htmls.append(r.text)return htmlsdef parse_single_html(html_doc):soup = BeautifulSoup(html_doc, "html.parser")article_items = (soup.find("div", class_="article").find("ol", class_="grid_view").find_all("div", class_="item"))datas = []for item in article_items:rank = item.find("div", class_="pic").find("em").get_text()info = item.find("div", class_="info")title = info.find("div", class_="hd").find("span", class_="title").get_text()stars = (info.find("div", class_="bd").find("div", class_="star").find_all("span"))rating_star = stars[0]["class"][0]rating_num = stars[1].get_text()comments = stars[3].get_text()datas.append({"rank": rank,"title": title,"rating_star": rating_star.replace("rating", "").replace("-t", ""),"rating_num": rating_num,"comments": comments.replace("人评价", "")})return datasdef save_to_excel(all_datas):"""保存解析后数据到excel:param all_datas: 传人数据:return:"""# print tabledf = pd.DataFrame(all_datas)print(df)df.to_excel("豆瓣电影TOP250.xlsx")if __name__ == '__main__':# 模拟用户行为headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"}# 定义根网址urlroot_url = "https://movie.douban.com/top250"htmls = download_all_htmls(root_url, headers)# beatiful print textpprint.pprint(parse_single_html(htmls[0]))# extend迭代添加到list,保存到excelall_datas = []for html in htmls:all_datas.extend(parse_single_html(html))print(len(all_datas))save_to_excel(all_datas)