- 基本的爬虫工作原理
- 基本的http抓取原理,scrapy
- Bloom Filter:
- 分布式爬虫概念
- rq和Scrapy的结合
- 后续处理,网页析取
假如是一只蜘蛛,爬到第一个页面,然后把页面全部抄下来,用脑子存下所看过的页面地址,每次想爬一个新链接都要去查一下这个页面是否去过,去过就不去了。(地址判重)
流程
发送请求–获得页面–解析页面–下载内容–存储内容。
准备内容:
list,dict:用来序列化你爬的东西
切片:用来对爬取的内容进行分割,生成
条件判断(if等):用来解决爬虫过程中哪些要哪些不要的问题
循环和迭代(for while ):用来循环,重复爬虫动作
文件读写操作:用来读取参数、保存爬下来的内容等
url管理器:管理将要爬取的url和已经爬取的url,防止重复抓取和循环抓取。
网页下载器
网页解析器
具体流程
将url管理器中待爬取的url送给网页下载器,网页下载器将其存储下来存为一个字符串,传送给网页解析器进行解析,每个网页都有指向其他网页的url也可以将其解析之后送给url管理器。
形成循环
爬虫架构动态工作流程
- 调度器去问url管理器有没有待爬取的url,返回是或否给调度器,如果是的话,调度器从url管理器中取出一个待爬取的url,url管理器将这个url返回给调度器。
- 现在调度器得到了这个url,调度器将url传送给下载器下载内容,下载好之后将url内容返回给调度器。
- 调度器将url的内容传送给解析器进行网页解析。解析之后返回有价值的数据和新的url列表。
- 一方面调度器将价值数据传送给应用进行数据收集,另一方面将新的url列表补充到url管理器。
- 如果url管理器还有新的待爬url,就将其循环。
- 等到url管理器没有待爬的url,将其价值数据发到应用上。
网页下载器urllib下载的三种方法:
1. python 3.x中urllib库和urilib2库合并成了urllib库。
其中urllib2.urlopen()变成了urllib.request.urlopen()
urllib2.Request()变成了urllib.request.Request()
import urllib #加载urllib模块
response = urllib.request.urlopen("http://www.baidu.com")#直接请求
print(response.getcode())#获取网页的返回码
cont = response.read()#读取内容
print(cont)
- 添加data,http header
向服务器提交需要用户输入的数据,将url,data,http header三个参数传送给urllib.request.Request()类,生成一个request对象,然后依然用urllib.request.urlopen打开
,以request作为参数。
import urllib.request
url = 'https://www.zhihu.com/'
req = urllib.request.Request(url)
req.add_data('a','1')#添加数据
req.add_header('User-Agent','Mozilla')#伪装成浏览器
f = urllib.request.urlopen(req)
print(f.read().decode('utf-8'))
3添加特殊情景的处理器
有些网页需要用户登录才能访问,有些网页需要代理才能访问,还有一些网页是用https加密的,
++Cookie,有时也用其复数形式 Cookies,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。++
import http.cookiejar, urllib
cj = http.cookiejar.CookieJar()#创建cookie容器
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))#创建一个opener
urllib.request.install_opener(opener)#给urllib安装opener
url = 'https://www.zhihu.com/'
response = urllib.request.urlopen(url)
print(response.read())
网页解析器:从网页中提取有价值数据的工具
结构化解析,通过一个树来进行遍历。
创建beautifulsoup对象,搜索节点find_all,find,访问节点,名称,属性,文字。
find_all(name,attrs,string)#节点的名称,节点的属性,节点的文本
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc,'html.parser')
links = soup.find_all('a')
for link in links:
print(link.name,link['href'],link.get_text())
link_node = soup.find('a',href='http://example.com/lacie')
print(link_node.name,link_node['href'],link_node.get_text())
正则匹配
p_node = soup.find('p',class_="title")
print(p_node.name,p_node.get_text())
实例爬虫
- 确定目标
抓取百度百科python词条以及相关词条页面的标题和简介
- 分析目标
- 抓取目标页面的url格式,限定抓取页面的范围
- 分析抓取数据的格式,本实例就是抓取标题和简介
- 分析页面的编码
- 编写代码
- 执行代码
入口页
http://baike.baidu.com/item/Python
新建一个python package
# main
from baike_spider import html_downloader
from baike_spider import html_outputer
from baike_spider import html_parser
from baike_spider import url_manager
class SpiderMain(object):
def __init__(self):
self.urls = url_manager.UrlManager()
self.downloader = html_downloader.HtmlDownloader()
self.parser = html_parser.HtmlParser()
self.outputer = html_outputer.HtmlOutputer()
def craw(self, root_url):
count = 1
self.urls.add_new_url(root_url)
while self.urls.has_new_url():#待爬取url不为空
try:
new_url = self.urls.get_new_url()#取出待爬取URL集合中的一个url
print('craw %d : %s'%(count, new_url))
html_cont = self.downloader.download(new_url)#将这个url指向的网页下载下来
new_urls, new_data = self.parser.parse(new_url, html_cont)
self.urls.add_new_urls(new_urls)
self.outputer.collect_data(new_data)
if(count == 1000):
break
count = count+1
except:
print('craw failed')
self.outputer.output_html()
if __name__=="__main__":
root_url = "http://baike.baidu.com/item/%E4%BA%92%E8%81%94%E7%BD%91/199186"
obj_spider = SpiderMain()
obj_spider.craw(root_url)
其余的是module
#url管理器
class UrlManager(object):
def __init__(self):
self.new_urls = set()#待爬取的url集合
self.old_urls = set()#已爬取的url集合
def add_new_url(self, url):#添加新的url
if url is None:
return
if url not in self.new_urls and url not in self.old_urls:#如果这个url不在待爬取的url中也不在已爬取的url中
self.new_urls.add(url)#那么就将其添加到待爬取的url集合中
def add_new_urls(self, urls):#添加url集合
if urls is None or len(urls) == 0:
return
for url in urls:
self.add_new_url(url)#进行单个的添加
def has_new_url(self):#判断待爬取的url集合是否为空
return len(self.new_urls) != 0#不为空的话return1 为空的话return0
def get_new_url(self):#取出待爬取集合中的url
new_url = self.new_urls.pop()
self.old_urls.add(new_url)#将这个url添加到已爬取的url集合中
return new_url
#网页下载器
import urllib.request
class HtmlDownloader(object):
def download(self, url):
if url is None:
return None
response = urllib.request.urlopen(url)#将url下载
if response.getcode() != 200:#网页访问失败
return None
return response.read()#返回网页的字符串
#网页解析器
from bs4 import BeautifulSoup
import re
from urllib.parse import urljoin
class HtmlParser(object):
def _get_new_urls(self, page_url, soup):
new_urls = set()
links = soup.find_all('a',href = re.compile(r"/view/\d+\.htm"))
for link in links:
new_url = link['href']
new_full_url = urljoin(page_url,new_url)
new_urls.add(new_full_url)
return new_urls
def _get_new_data(self, page_url, soup):
res_data = {}
res_data['url'] = page_url
#<dd class="lemmaWgt-lemmaTitle-title">
#<h1>Python</h1>
title_node = soup.find('dd',class_="lemmaWgt-lemmaTitle-title").find("h1")
res_data['title'] = title_node.get_text()
#<div class="lemma-summary" label-module="lemmaSummary">
summary_node = soup.find('div',class_ = "lemma-summary")
res_data['summary'] = summary_node.get_text()
return res_data
def parse(self, page_url, html_cont):
if page_url is None or html_cont is None:
return
soup = BeautifulSoup(html_cont,'html.parser')
new_urls = self._get_new_urls(page_url,soup)
new_data = self._get_new_data(page_url,soup)
return new_urls, new_data
#输出
class HtmlOutputer(object):
def __init__(self):
self.datas = []
def collect_data(self, data):
if data is None:
return
self.datas.append(data)
def output_html(self):
fout = open('output.html','w',encoding ='utf-8')
fout.write("<html>")
fout.write("<body>")
fout.write("<table>")
for data in self.datas:
fout.write("<tr>")
fout.write("<td>%s</td>" % data['url']+'\n')
fout.write("<td>%s</td>" % data['title']+'\n')
fout.write("<td>%s</td>" % data['summary']+'\n')
fout.write("/<table>")
fout.write("/<body>")
fout.write("/<html>")