爬虫基础

中秋三天,把爬虫一些基础的东西过了一遍,还没有过完,等以后有时间了再来继续
Day1******************************************************************

1、网络爬虫

1、定义 :网络蜘蛛、网络机器人,抓取网络数据的程序
2、总结 :用Python程序去模仿人去访问网站,模仿的越逼真越好
3、爬取数据的目的 :通过有效的大量数据分析市场走势、公司决策

2、企业获取数据的方式

1、公司自有数据
2、第三方数据平台购买
数据堂、贵阳大数据交易所
3、爬虫爬取数据
市场上没有或者价格太高,利用爬虫程序爬取

3、Python做爬虫优势

请求模块、解析模块丰富成熟,强大的scrapy框架
PHP :对多线程、异步支持不太好
JAVA :代码笨重,代码量很大
C/C++ :虽然效率高,但是代码成型很慢

4、爬虫分类

1、通用网络爬虫(搜索引擎引用,需要遵守robots协议)

http://www.taobao.com/robots.txt
1、搜索引擎如何获取一个新网站的URL
  1、网站主动向搜索引擎提供(百度站长平台)
  2、和DNS服务网(万网),快速收录新网站

2、聚焦网络爬虫

自己写的爬虫程序:面向主题的爬虫、面向需求的爬虫

5、爬取数据步骤:

1)、确定需要爬取的URL地址

2)、通过HTTP/HTTPS协议来获取相应的HTML页面

3)、提取HTML页面有用的数据

    1、所需数据,保存
    2、页面中有其他的URL,继续 第2步

6、Anaconda和Spyder

1)、Anaconda :开源的Python发行版本

2)、Spyder :集成开发环境

Spyder常用快捷键:
  1、注释/取消注释 :ctrl + 1(数字)
  2、保存 :ctrl + s
  3、运行程序 :f5
  4、自动补全 :Tab

7、Chrome浏览器插件

1)、安装步骤

1、右上角 - 更多工具 - 扩展程序
2、点开右上角 - 开发者模式
3、把插件 拖拽到 浏览器页面,释放鼠标,点击 添加扩展...

2、插件介绍

1、Proxy SwitchOmega :代理切换插件
2、Xpath Helper :网页数据解析插件
3、JSON View    :查看json格式的数据(好看)

8、Fiddler抓包工具

添加证书信任的方法:证书生成器(fiddlercertmaker.exe)

1)、抓包工具设置

1、Tools->options->HTTPS->...from browers only
2、connections :设置端口号 8888

2)、设置浏览器代理

Proxy SwitchOmega -> 选项 -> 新建情景模式 -> HTTP 127.0.0.1 8888 -> 应用选项

浏览器右上角图标 -> AID1806 -> 访问百度

9、WEB

1)、HTTP和HTTS

1、HTTP :80
2、HTTPS :443,HTTP的升级版,加了一个安全套接层(socket)

2)、GET和POST

1、GET :查询参数都会在URL上显示出来
2、POST:查询参数和需要提交数据是隐藏在Form表单里的,不会在URL地址上显示

3)、URL :统一资源定位符

https://  item.jd.com  :80   /26809408972.html #detail
 协议     域名/IP地址  端口  访问资源的路径    锚点(data-anchor:写哪跳到哪)

4)、User-Agent

记录用户的浏览器、操作系统等,为了让用户获取更好的HTML页面效果
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
我的电脑:
`User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36`
User-Agent大全
Mozilla Firefox :(Gecko内核)
IE :Trident(自己的内核)
Linux : KTHML(like Gecko)
Apple :Webkit(like KHTML)
Google:Chrome(like Webkit)
其他浏览器都是模仿IE/Chrome

10、爬虫请求模块:发请求-响应——解析

1)、版本

1、python2 :urllib2、urllib
2、python3 :把urllib和urllib2合并,urllib.request

2)、常用方法

1、urllib.request.urlopen("网址")
例子:
import urllib.request

# response为响应对象
response = urllib.request.urlopen("http://www.baidu.com/")
html = response.read().decode("utf-8")

print(html)

  1、作用 :向网站发起一个请求并获取响应
    字节流 = response.read()
    字符串 = response.read().decode("utf-8")

    #encode()  字符串 --> bytes数据类型
	#decode()  bytes数据类型 --> 字符串

  2、重构User-Agent
    1、不支持重构User-Agent :urlopen()
    2、支持重构User-Agent
      urllib.request.Request(添加User-Agent)
2、urllib.request.Request("网址",headers="字典")
  User-Agent是爬虫和反爬虫斗争的第一步,发送请求必须带User-Agent
  1、使用流程
    1、利用Request()方法构建请求对象
	2、利用urlopen()方法获取响应对象
	3、利用响应对象的read().decode("utf-8")获取内容
	例子:
		url = "https://www.baidu.com/"
		headers = {"User-Agent":"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"}
		import urllib.request
		#1.创建一个请求对象(User-Agent)
		req = urllib.request.Request(url,headers = headers)
		#2.获取响应对象(urlopen())
		res = urllib.request.urlopen(req)
		#3.响应对象read().decode("utf-8")
		html = res.read().decode("utf-8")
		print(html)
  2、响应对象response的方法
    1、read() :读取服务器响应的内容
	2、getcode()作用:返回HTTP的响应码
		  print(respones.getcode())
		  200 :成功
		  4XX :服务器页面出错
		  5XX :服务器出错
	3、geturl()作用 :返回实际数据的URL(防止重定向问题)
	例子:
	import urllib.request
	url = "http://www.baidu.com/"
	headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"}
	# 1.创建请求对象(有User-Agent)
	req = urllib.request.Request(url,headers=headers)
	# 2.获取响应对象(urlopen())
	res = urllib.request.urlopen(req)
	# 3.响应对象read().decode("utf-8")
	#html = res.read().decode("utf-8")
	#print(html)
	print(res.getcode())
	print(res.geturl())
3、urllib.parse模块
secret:%E8%B0%A2%E7%A6%B9%E6%95%8F
	1、urlencode(字典)  ## 注意:参数一定要为字典
	   urlencode({"wd":"美女"})
	   wd=%e8.......
	   tedu = {"wd":"万科"}
	   示例见 :04_urllib.parse.urlencode示例.py
	   #%E8%B0%A2%E7%A6%B9%E6%95%8F
		import urllib.parse
		tedu = {"miss":"miss you"}
		tedu = urllib.parse.urlencode(tedu)
		print(tedu)

	 请输入你要搜索的内容:美女
	 保存到本地文件 :美女.html
	 代码:
		import urllib.request
		import urllib.parse
		#拼接URL
		baseurl = "http://www.baidu.com/s?"
		key = input("请输入要搜索的内容:")
		#进行URLencode()编码
		wd = {"wd":key}
		key = urllib.parse.urlencode(wd)
		url = baseurl + key
		headers = {"User_Agebt":"MOzilla/5.0"}
		#创建请求对象
		req = urllib.request.Request(url,headers = headers)
		#获取响应对象
		res = urllib.request.urlopen(req)
		html = res.read().decode("utf-8")
		#写入本地文件
		with open("搜索.html","w",encoding = "gb18030") as f:
			f.write(html)

	2、quote(字符串) 
	  key = urllib.parse.quote("字符串")
	  baseurl = "http://www.baidu.com/s?wd="
	  key = input("请输入要搜索的内容:")
	  #进行quote()编码
	  key = urllib.parse.quote(key)
	  url = baseurl + key
	  print(url)
	  例子:
		import urllib.request
		import urllib.parse
		#拼接URL
		baseurl = "http://www.baidu.com/s?wd="
		key = input("请输入要搜索的内容:")
		#进行quote()编码

		key = urllib.parse.quote(key)
		url = baseurl + key
		print(url)
		headers = {"User_Agebt":"MOzilla/5.0"}
		#创建请求对象
		req = urllib.request.Request(url,headers = headers)
		#获取响应对象
		res = urllib.request.urlopen(req)
		html = res.read().decode("utf-8")
		#写入本地文件
		with open("搜索2.html","w",encoding = "gb18030") as f:
			f.write(html)

4、练习

百度贴吧数据抓取
要求:
  1、输入要抓取的贴吧名称
  2、输入爬取的起始页和终止页
  3、把每一页的内容保存到本地
    第1页.html 第2页.html ... ... 
步骤:
  1、找URL规律,拼接URL
    第1页:http://tieba.baidu.com/f?kw=??&pn=0
	第2页:http://tieba.baidu.com/f?kw=??&pn=50
	第3页:http://tieba.baidu.com/f?kw=??&pn=100
.....
	第n页:pn=(n-1)*50

  2、获取网页内容(发请求获响应)
  3、保存(本地文件、数据库)
  例子:易烊千玺百度贴吧抓取
import urllib.request
import urllib.parse
import random
import time
#随机获取一个User-Agent
header_list = [{"User-Agent":"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"},
				 {"User-Agent":"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)"},
				 {"User-Agent":"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)"}]
 
headers = random.choice(header_list)
#主体程序
name = input("请输入贴吧名:")
begin = int(input("请输入起始页:"))
end = int(input("请输入终止页:"))
#对贴吧名name进行编码
kw = {"kw":name}
kw = urllib.parse.urlencode(kw)
#拼接url,发请求,获响应
for i in range(begin,end + 1):
	#拼接url
	pn = (i -1) * 50
	url = "http://tieba.baidu.com/f?" + kw + "&pn=" + str(pn)
	#http://tieba.baidu.com/f?kw=??&pn=0
	#发请求
	req = urllib.request.Request(url,headers = headers)
	res = urllib.request.urlopen(req,timeout = 5)
	time.sleep(2)
	html = res.read().decode("utf-8")
	#写入文件
	filename = "第" + str(i) + "页.html"
	with open(filename,"w",encoding="utf-8") as f:
		print("正在爬取第%d页" % i)
		f.write(html)
		print("第%d页爬取成功" % i)
		print("*" * 30)

11、请求方式及实例

1)、GET

1、特点 :查询参数在URL地址中显示
2、案例 :抓取百度贴吧

2)、POST(在Request方法中添加data参数)

1、urllib.request.Request(url,data=data,headers=headers)
data :表单数据以bytes类型提交,不能是str
2、处理表单数据为bytes类型
  1、把Form表单数据定义为字典data
  2、urlencode(data)
  3、转为bytes数据类型 :bytes()
3、有道翻译案例
代码:
import urllib.request
import urllib.parse
#请输入你要翻译的内容
key = input("请输入要翻译的内容:")
#把提交的form表单数据转为bytes数据类型
data = {"i": key,
		"from": "AUTO",
		"to": "AUTO",
		"smartresult": "dict",
		"client": "fanyideskweb",
		"salt": "15683634125285",
		"sign": "44c86c0701f282f0c07bc8020847b2a6",
		"ts": "1568363412528",
		"bv": "53539dde41bde18f4a71bb075fcf2e66",
		"doctype": "json",
		"version": "2.1",
		"keyfrom": "fanyi.web",
		"action": "FY_BY_REALTlME",
		"typoResult":"false"
		}
#字符串 i = python& from =auto...
data = urllib.parse.urlencode(data)
data = bytes(data,"utf-8")
#发请求,获取响应
#url为POST的地址,抓包工具抓到的,此处去掉_o
url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"
headers = {"User-Agent":"Mozilla/5.0"}
#此处data为form表单数据,为bytes数据类型
req = urllib.request.Request(url,data = data,headers = headers)
res = urllib.request.urlopen(req)
html = res.read().decode("utf-8")
print(html)
4、有道翻译返回的是json格式的字符串,如何把json格式的字符串转换为Python中字典
  import json

  r_dict = json.loads(r_json)

day2*******************************************************************************

1、解析

1、数据的分类

1、结构化数据
  特点 :有固定的格式,如 :HTML、XML、JSON
2、非结构化数据
  示例 :图片、音频、视频,这类数据一般都存储为二进制

2、正则表达式re

1、使用流程
  1、创建编译对象 :p = re.compile("正则表达式")
  2、对字符串匹配 :r = p.match("字符串")
  3、获取匹配结果 :print(r.group())
2、常用方法
  1、match(s) :字符串开头的第1个,返回对象
  2、search(s):从开始往后找,匹配第1个,返回对象
  3、group()  :从match或search返回对象中取值
  4、findall():所有全部匹配,返回一个列表
3、表达式
  .  匹配任意字符(不能匹配\n)
  \d 数字
  \s 空白字符
  \S 非空白字符  
  [...] 包含[]内容 :A[BCD]E  --> ABE  ACE  ADE 
  \w 字母、数字、_

  *  前一个字符出现0次或多次
  ?  前一个字符出现0次或1次
  +  前一个字符出现1次或多次
  {m} 前一个字符出现m次
  {m,n} 前一个字符出现m-n次  AB{1,3}C --> ABC ABBC ABBBC

  贪婪匹配(.*) :在整个表达式匹配成功的前提下,尽可能多的匹配*

  非贪婪匹配(.*?) :在整个表达式匹配成功的前提下,尽可能少的匹配*
4、示例(贪婪模式和非贪婪模式)
  见 :见01_贪婪匹配和非贪婪匹配示例.py
	import re
	s = """<div><p>众芳摇落独喧妍,占尽风情向小园</div></p>
	<div><p>疏影横斜水清浅,暗香浮动月黄昏</div></p>"""
	#创建编译对象,贪婪匹配
	#re.S作用:使.能够以匹配\n在内的所有字符
	#贪婪匹配是.*,
	p = re.compile('<div><p>.*</div></p>',re.S)
	#非贪婪匹配:.*?
	p = re.compile('<div><p>.*?</div></p>',re.S)
	#匹配字符串s
	r = p.findall(s)
	print(r)
5、findall()的分组(重要,但我没听懂)
import re
#解释 :先按照整体匹配出来,然后再匹配()中的
# 如果有2个或者多个(),则以元组的方式取显示

s = "A B C D"
p1 = re.compile('\w+\s+\w+')
print(p1.findall(s))
# ['A B','C D']

p2 = re.compile('(\w+)\s+\w+')
# 第1步 :['A B','C D']
# 第2步 :['A','C']
print(p2.findall(s))

p3 = re.compile('(\w+)\s+(\w+)')
# 第1步 :['A B','C D']
# 第2步 :[('A','B'),('C','D')]
print(p3.findall(s))
 6、练习
import re
s = """<div class = "animal">
    <p class = "name">
        <a title = "Tiger"></a>
    </p>
    <p class = "contents">
        Two tigers two tigers run fast
    </p>
</div>

<div class = "animal">
    <p class = "name">
        <a title = "Rabbit"></a>
    </p>
    <p class = "contents">
        Small white rabbit white and white
    </p>
</div>"""
#正则表达式
p = re.compile('<div class = "animal".*?title = "(.*?)">.*?contents">(.*?)</p>',re.S)
r = p.findall(s)
#print(r)
for animal in r:
    print("动物名称:",animal[0].strip())
    print("动物描述:",animal[1].strip())
7、案例1 :内涵段子脑筋急转弯抓取
  见 :04_内涵段子脑筋急转弯抓取.py
  网址 :www.neihan8.com
  步骤:
    1、找URL规律
 第1页:https://www.neihan8.com/njjzw/
 第2页:https://www.neihan8.com/njjzw/index_2.html
 第3页:https://www.neihan8.com/njjzw/index_3.html
2、用正则匹配出 题目 和 答案
  p = re.compile('<div class="text-.*?title="(.*?)".*?<div class="desc">(.*?)</div>',re.S)
3、写代码
  1、发请求
  2、用正则匹配
  3、写入本地文件
  代码:
import urllib.request
import re

class NeihanSpider:
    def __init__(self):
        self.baseurl = "https://www.neihan8.com/njjzw/"
        self.headers = {"User-Agent":"Mozilla/5.0"}
        self.page = 1
        
    # 下载页面
    def loadPage(self,url):
        req = urllib.request.Request(url,headers=self.headers)
        res = urllib.request.urlopen(req)
        html = res.read().decode("utf-8")
        self.parsePage(html)
        
    # 解析页面
    def parsePage(self,html):
        p = re.compile('<div class="text-.*?title="(.*?)".*?<div class="desc">(.*?)</div>',re.S)
        r_list = p.findall(html)
        # [("什么动物...","海豹"),(),()...]
        self.writePage(r_list)
        
    # 保存页面
    def writePage(self,r_list):
        for r_tuple in r_list:
            for r_str in r_tuple:
                with open("急转弯.txt","a",encoding="gb18030") as f:
                    f.write(r_str.strip() + "\n")
            with open("急转弯.txt","a",encoding="gb18030") as f:
                f.write("\n")
        
    def workOn(self):
        self.loadPage(self.baseurl)
        while True:
            c = input("成功,是否继续(y/n):")
            if c.strip().lower() == "y":
                self.page += 1
                url = self.baseurl + "index_" +\
                      str(self.page) + ".html"
                self.loadPage(url)
            else:
                print("爬取结束,谢谢使用!")
                break
                
if __name__ == "__main__":
    spider = NeihanSpider()
    spider.workOn()
    

    








8、猫眼电影top100榜单,存到csv表格文件中
  网址:猫眼电影 - 榜单 - top100榜
  目标:抓取电影名、主演、上映时间
  1、知识点讲解
    1、csv模块的使用流程
  1、打开csv文件
    with open("测试.csv","a") as f:
  2、初始化写入对象
    writer = csv.writer(f)
  3、写入数据
    writer.writerow(列表)
2、示例 
'''05_csv示例.py'''
import csv

with open("猫眼.csv","a",newline="") as f:
    # 初始化写入对象
    writer = csv.writer(f)
    #把列表写入到文件中
    writer.writerow(["电影名称","主演"])
    writer.writerow(["霸王别姬","张国荣"])
    writer.writerow(["唐伯虎点秋香","周星驰"])

  2、准备工作
    1、找URL
  第1页:http://maoyan.com/board/4?offset=0
  第2页:http://maoyan.com/board/4?offset=10
  第n页:
    offset = (n-1)*10
2、正则匹配
  <div class="movie-item-info">.*?title="(.*?)".*?<p class="star">(.*?)</p>.*?releasetime">(.*?)</p>
3、写代码
代码:
'''06_猫眼电影top100抓取.py'''
import urllib.request
import re
import csv

class MaoyanSpider:
    def __init__(self):
        self.baseurl = "http://maoyan.com/board/4?offset="
        self.headers = {"User-Agent":"Mozilla/5.0"}
        self.page = 1
        self.offset = 0
        
    # 下载页面
    def loadPage(self,url):
        req = urllib.request.Request(url,headers=self.headers)
        res = urllib.request.urlopen(req)
        html = res.read().decode("utf-8")
        self.parsePage(html)
        
    # 解析页面
    def parsePage(self,html):
        p = re.compile('<div class="movie-item-info">.*?title="(.*?)".*?<p class="star">(.*?)</p>.*?releasetime">(.*?)</p>',re.S)
        r_list = p.findall(html)
#        print(r_list)
        # [("霸王别姬","张国荣","1994-01-01"),(),()...]
        self.writePage(r_list)
    
    def writePage(self,r_list):
        if self.page == 1:
            with open("猫眼电影.csv","a",newline="") as f:
                writer = csv.writer(f)
                writer.writerow(["电影名称","主演","上映时间"])
        for r_tuple in r_list:
            with open("猫眼电影.csv","a",newline="") as f:
                # 创建写入对象
                writer = csv.writer(f)
#                L = list(r_tuple)
                L = [r_tuple[0].strip(),r_tuple[1].strip(),r_tuple[2].strip()]
                # ["霸王别姬","张国荣","1994-01-01"]
                writer.writerow(L)
    
    def workOn(self):
        while True:
            c = input("爬取请按y(y/n):")
            if c.strip().lower() == "y":
                self.offset = (self.page-1)*10
                url = self.baseurl + str(self.offset)
                self.loadPage(url)
                self.page += 1
            else:
                print("爬取结束,谢谢使用!")
                break
                
if __name__ == "__main__":
    spider = MaoyanSpider()
    spider.workOn()     
        
        
        
        
        
        
        
        
        

2、Fiddler常用菜单

1)、Inspector : 查看抓到的数据包的详细内容

1、分为请求(request)和响应(response)两部分

2、常用选项

1、Headers :显示客户端发送到服务器的header,包含客户端信息、cookie、传输状态
2、WebForms :显示请求的POST数据 <body>
3、Raw :将整个请求显示为纯文本

3)、请求方式及案例

  1、GET
  2、POST
  3、Cookie模拟登陆
1、什么是cookie、session
  HTTP是一种无连接协议,客户端和服务器交互仅仅限于 请求/响应过程,结束后断开,下一次请求时,服务器会认为是一个新的客户端,为了维护他们之间的连接,让服务器知道这是前一个用户发起的请求,必须在一个地方保存客户端信息。
  cookie :通过在客户端记录的信息确定用户身份
  session :通过在服务端记录的信息确定用户身份
2、案例 :使用cookie模拟登陆人人网
  见 :07_cookie模拟登陆人人网.py
  步骤:
    1、通过抓包工具、F12获取到cookie(先登陆1次网站)
	2、正常发请求
	url:http://www.renren.com/967469305/profile
	代码:
'''07_cookie模拟登陆人人网.py'''
import urllib.request

url = "http://www.renren.com/967469305/profile"
headers = {
        "Host":"www.renren.com",
        "Connection":"keep-alive",
        "Upgrade-Insecure-Requests":"1",
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
        "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "Referer":"http://www.renren.com/",
#        Accept-Encoding: gzip, deflate
        "Accept-Language":"zh-CN,zh;q=0.9",
        "Cookie":"anonymid=jnoaljpk7d3nh2; depovince=BJ; _r01_=1; _de=4DBCFCC17D9E50C8C92BCDC45CC5C3B7; ln_uact=13603263409; ln_hurl=http://head.xiaonei.com/photos/0/0/men_main.gif; jebe_key=1b1f4a34-0468-4185-a3b0-6f2c38abc368%7C2012cb2155debcd0710a4bf5a73220e8%7C1540454149943%7C1%7C1540454153787; wp_fold=0; wp=0; jebecookies=2fc339e7-1b51-43ce-bc85-e2dc1f68ee16|||||; JSESSIONID=abcANrnqoMuLshY34pQAw; ick_login=30d0bd58-f6bb-437f-8d0d-6a72ae00e7b7; p=1e1b85cb8dda387a70e400a341c2e9c95; first_login_flag=1; t=4f652cc0a8f3fd50f5c9095c92d4717d5; societyguester=4f652cc0a8f3fd50f5c9095c92d4717d5; id=967469305; xnsid=55bff2d5; loginfrom=syshome"
    }

req = urllib.request.Request(url,headers=headers)
res = urllib.request.urlopen(req)
print(res.read().decode("utf-8"))

'''cookie模拟登录优快云网'''
import urllib.request

url = "https://i.youkuaiyun.com/#/uc/profile"

headers = {
        "Host":"www.youkuaiyun.com",
        "Connection":"keep-alive",
        "Upgrade-Insecure-Requests":"1",
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36x-requested-with:XMLHttpRequest",
        "Accept":"application/json, text/plain, */*",
        "Referer":"//passport.youkuaiyun.com/login?code=public",
#        Accept-Encoding: gzip, deflate
        "Accept-Language":"zh-CN,zh;q=0.9",
        "Cookie":"uuid_tt_dd=10_9868640580-1568772966185-773923; dc_session_id=10_1568772966185.480725; SESSION=2c6a7725-b303-466b-9e16-24802c6d133e; __guid=165585982.4138767749505633000.1568772966482.5442; monitor_count=1; UserName=BTZywl; UserInfo=b28e65a2eb3447e9886eca80ef825a61; UserToken=b28e65a2eb3447e9886eca80ef825a61; UserNick=%E5%8C%85%E5%B0%8F%E5%BD%A4; AU=E46; UN=BTZywl; BT=1568772981929; p_uid=U000000; Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac=1568701652,1568705441,1568715718,1568772986; Hm_lpvt_6bcd52f51e9b3dce32bec4a3997715ac=1568772986; Hm_ct_6bcd52f51e9b3dce32bec4a3997715ac=1788*1*PC_VC!5744*1*BTZywl!6525*1*10_9868640580-1568772966185-773923; dc_tos=py08zk"
    }
req = urllib.request.Request(url,headers = headers)
res = urllib.request.urlopen(req)
print(res.read().decode("utf-8"))

在这里插入图片描述
这里的url = "https://i.youkuaiyun.com/#/uc/profile"是自己的个人中心的网址,刚开始还不知道怎么弄,后来想了一下,试了试这个,竟然对了,还有一个需要改的地方是 “Host”:“www.youkuaiyun.com”,最后记得cookie当然是要改的了,多做一两个就明白了,果然睡醒之后的大脑是很聪明的。

4、requests模块

1、安装(用管理员身份去打开Anaconda Prompt)

Anaconda   : conda install requests
Windows cmd: python -m pip install requests
  ## 以管理员身份去执行pip安装命令

2、常用方法

1、get(url,headers=headers) : 发起请求,获取响应对象
2、response属性
  1、response.text :返回字符串类型
  2、response.content : 返回bytes类型
    1、应用场景 :爬取非结构化数据
	2、示例:将网上的图片爬取到本地
	代码:
# -*- coding: utf-8 -*-
"""
Created on Sat Sep 14 16:32:45 2019

@author: aa
"""
import requests
url = "https://media.tenor.com/images/aab76b33f0da6f6841a6c063e3d89e78/tenor.gif"
headers = {"User-Agent":"Mozilla/5.0"}
res = requests.get(url,headers = headers)
html = res.content
with open("易烊千玺.jpg","wb") as f:
    f.write(html)
    
print("图片下载成功")

这里需要注意的是图片的地址并不是导航栏上的地址,而是需要右键—>审查元素–>里面找到图片的地址,不然很可能爬不下来,我爬取大佬的照片的时候就遇到这个小问题了。
3、response.encoding :响应对象编码
一般返回 :ISO-8859-1
response.encoding = “utf-8”
4、response.status_code :返回服务器响应码
5、response.url :返回数据的URL地址
3、get()使用场景
1、没有查询参数
res = requests.get(url,headers=headers)
2、有查询参数: params={}
注 :params参数必须为字典,自动进行编码
见 :09_requests.get.params.py
代码:

'''09_requests.get.params.py'''
import requests

headers = {"User-Agent":"Mozilla/5.0"}
url = "http://www.baidu.com/s?"
key = input("请输入要搜索的内容:")
params = {"wd":key}

# 自动编码,自动拼接URL,params必须为字典
res = requests.get(url,params=params,headers=headers)
# 指定utf-8
res.encoding = "utf-8"
print(res.text)
4、post() 参数名 :data
  1、data = {}
  2、示例 :10_有道翻译post.py
  代码:
'''10_有道翻译post.py'''
import requests
import json

# 请输入你要翻译的内容
key = input("请输入要翻译的内容:")
# post方法要求data为字典格式
data = {"i": key,
        "from":"AUTO",
        "to":"AUTO",
        "smartresult":"dict",
        "client":"fanyideskweb",
        "salt":"1540373170893",
        "sign":"a5d9b838efd03c9b383dc1dccb742038",
        "doctype":"json",
        "version":"2.1",
        "keyfrom":"fanyi.web",
        "action":"FY_BY_REALTIME",
        "typoResult":"false"
    }

# 发请求,获取响应
# url为POST的地址,抓包工具抓到的,此处去掉 _o
url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"
headers = {"User-Agent":"Mozilla/5.0"}
# 此处data为form表单数据
res = requests.post(url,data=data,headers=headers)
res.encoding = "utf-8"
html = res.text
# 把json格式字符串转换为Python中字典
r_dict = json.loads(html)
result = r_dict['translateResult'][0][0]["tgt"]
print(result)

Day3*************************************************************************

1、requests模块

1、代理(参数名:proxies)

1、获取代理IP的网站
  西刺代理网站
  快代理
  全网代理
2、普通代理
  proxies = {"协议":"协议://IP地址:端口号"}
183.129.207.82	11597
183.230.177.118	8060
www.whatismyip.com(一天只能访问5次)
查自己电脑上的ip:在终端输入ipconfig
3、私密代理(收费)
  proxies = {"http":"http://309435365:szayclhp@123.206.119.108:21081"}

2、案例1 :爬取链家二手房信息 --> 存到MySQL数据库中

1、找URL
  https://bj.lianjia.com/ershoufang/pg1/
2、正则
<div class="houseInfo".*?data-el="region">(.*?)</a>.*?<div="totalPrice">.*?<span>(.*?)</span>(.*?)</div>
3、写代码
代码:
'''05_链家数据ToMysql.py'''
import requests
import re
import pymysql
import warnings

class LianjiaSpider:
    def __init__(self):
        self.baseurl = "https://bj.lianjia.com/ershoufang/pg"
        self.page = 1
        self.headers = {"User-Agent":"Mozilla/5.0"}
        self.proxies = {"http":"http://309435365:szayclhp@123.206.119.108:16817"}
        self.db = pymysql.connect("localhost",
                  "root","123456",charset="utf8")
        self.cursor = self.db.cursor()

    def getPage(self,url):
        res = requests.get(url,proxies=self.proxies,headers=self.headers,timeout=5)
        res.encoding = "utf-8"
        html = res.text
        print("页面爬取成功,正在解析...")
        self.parsePage(html)

    def parsePage(self,html):
        p = re.compile('<div class="houseInfo".*?data-el="region">(.*?)</a>.*?<div class="totalPrice">.*?<span>(.*?)</span>(.*?)</div>',re.S)
        r_list = p.findall(html)
        # [("天通苑","480","万"),()..]
        print("页面解析完成,正在存入数据库...")
        self.writeTomysql(r_list)

    def writeTomysql(self,r_list):
        c_db = "create database if not exists Lianjiadb \
                character set utf8"
        u_db = "use Lianjiadb"
        c_tab = "create table if not exists housePrice( \
                 id int primary key auto_increment,\
                 housename varchar(50), \
                 totalprice int)charset=utf8"
        
        warnings.filterwarnings("ignore")
        try:
            self.cursor.execute(c_db)
            self.cursor.execute(u_db)
            self.cursor.execute(c_tab)
        except Warning:
            pass

        ins = "insert into housePrice(housename,totalprice) \
               values(%s,%s)"
        for r_tuple in r_list:
            name = r_tuple[0].strip()
            price = float(r_tuple[1].strip())*10000
            L = [name,price]
            self.cursor.execute(ins,L)
            self.db.commit()
        print("存入数据库成功")


    def workOn(self):
        while True:
            c = input("爬取按y(q退出):")
            if c.strip().lower() == "y":
                url = self.baseurl + str(self.page) + "/"
                self.getPage(url)
                self.page += 1
            else:
                self.cursor.close()
                self.db.close()
                print("爬取结束,谢谢使用!")
                break



if __name__ == "__main__":
    spider = LianjiaSpider()
    spider.workOn()
'''06_链家数据ToMongo.py'''
import requests
import re
import pymongo

class LianjiaSpider:
    def __init__(self):
        self.baseurl = "https://bj.lianjia.com/ershoufang/pg"
        self.page = 1
        self.headers = {"User-Agent":"Mozilla/5.0"}
        self.proxies = {"http":"http://309435365:szayclhp@123.206.119.108:16817"}
        self.conn = pymongo.MongoClient("localhost",27017)
        self.db = self.conn.Lianjia
        self.myset = self.db.housePrice

    def getPage(self,url):
        res = requests.get(url,proxies=self.proxies,headers=self.headers,timeout=5)
        res.encoding = "utf-8"
        html = res.text
        print("页面爬取成功,正在解析...")
        self.parsePage(html)

    def parsePage(self,html):
        p = re.compile('<div class="houseInfo".*?data-el="region">(.*?)</a>.*?<div class="totalPrice">.*?<span>(.*?)</span>(.*?)</div>',re.S)
        r_list = p.findall(html)
        # [("天通苑","480","万"),()..]
        print("页面解析完成,正在存入数据库...")
        self.writeTomongo(r_list)

    def writeTomongo(self,r_list):
        for r_tuple in r_list:
            D = {"houseName":r_tuple[0].strip(),\
            "totalPrice":float(r_tuple[1].strip())*10000}
            self.myset.insert(D)
        print("存入数据库成功")

    def workOn(self):
        while True:
            c = input("爬取按y(q退出):")
            if c.strip().lower() == "y":
                url = self.baseurl + str(self.page) + "/"
                self.getPage(url)
                self.page += 1
            else:
                print("爬取结束,谢谢使用!")
                break



if __name__ == "__main__":
    spider = LianjiaSpider()
    spider.workOn()

3、Web客户端验证(参数名:auth)

1、auth=("用户名","密码")(元组的方式)
   auth=("tarenacode","code_2013")
2、案例 :09_Web客户端验证.py
代码:
'''09_Web客户端验证.py'''
import requests
import re

class NoteSpider:
    def __init__(self):
        self.headers = {"User-Agent":"Mozilla/5.0"}
        self.url = "http://code.tarena.com.cn/"
        self.proxies = {"http":"http://309435365:szayclhp@123.206.119.108:16817"}
        # auth参数存储用户名和密码(必须为元组)
        self.auth = ("tarenacode","code_2013")
    
    def getParsePage(self):
        res = requests.get(self.url,
                           proxies=self.proxies,
                           headers=self.headers,
                           auth=self.auth,
                           timeout=3)
        res.encoding = "utf-8"
        html = res.text
        print(html)
        p = re.compile('<a href=".*?>(.*?)</a>',re.S)
        r_list = p.findall(html)
        print(r_list)
        self.writePage(r_list)
        
    def writePage(self,r_list):
        print("开始写入文件...")
        with open("达内科技.txt","a") as f:
            for r_str in r_list:
                f.write(r_str + "\n\n")
        print("写入成功")
    
if __name__ == "__main__":
    spider = NoteSpider()
    spider.getParsePage()

4、SSL证书认证(参数名:verify)

数字证书认证中心
1、verify = True : 默认,进行SSL证书认证
2、verify = False: 不做认证

代码:

import requests
url = "https://www.12306.cn/mormhweb/"
headers = {"User-Agent":"MOzilla/5.0"}

res = requests.get(url,headers = headers,verify = False)
res.encoding = "utf-8"
print(res.text)

2、urllib.request中Handler处理器

1、定义

自定义的urlopen()方法,urlopen()方法是一个特殊的opener(模块已定义好),不支持代理等功能,通过Handler处理器对象来自定义opener对象

2、常用方法

1、build_opener(Handler处理器对象) :创建opener对象
2、opener.open(url,参数)

3、使用流程

1、创建相关的Handler处理器对象
  http_handler = urllib.request.HTTPHandler()
2、创建自定义opener对象
  opener = urllib.request.build_opener(http_handler)
3、利用opener对象打开url
  req = urllib.request.Request(url,headers=headers)
  res = opener.open(req)

4、Handler处理器分类

1、HTTPHandler() :没有任何特殊功能
2、ProxyHandler(普通代理)
  代理: {"协议":"IP地址:端口号"}
3、ProxyBasicAuthHandler(密码管理器对象) :私密代理
4、HTTPBasicAuthHandler(密码管理器对象) : web客户端认证
5、密码管理器对象作用
  1、私密代理
  2、Web客户端认证
  3、程序实现流程
    1、创建密码管理器对象
  pwdmg = urllib.request.HTTPPasswordMgrWithDefaultRealm()
	2、把认证信息添加到密码管理器对象
  pwdmg.add_password(None,webserver,user,passwd)
	3、创建Handler处理器对象
		1、私密代理
    proxy = urllib.request.ProxyAuthBasicHandler(pwdmg)
		2、Web客户端
    webbasic = urllib.request.HTTPBasicAuthHandler(pwdmg)

安装:

1、Windows :安装selenium

Anaconda Prompt下执行 : 
    python -m pip install selenium

2、Ubuntu :安装Scrapy框架

#### 依赖库较多,以下为全部依赖库,有些已安装 ####
1、sudo apt-get install libssl-dev
   sudo apt-get install libffi-dev 
   sudo apt-get install python3-dev
   sudo apt-get install build-essential
   sudo apt-get install libxml2
   sudo apt-get install libxml2-dev
   sudo apt-get install libxslt1-dev
   sudo apt-get install zlib1g-dev
2、sudo pip3 install Scrapy

Day04****************************************************************

1、xpath工具(解析)

1、xpath

本来是在XML文档中查找信息的语言,同样适用于HTML文档的检索

2、xpath辅助工具

1、Chrome插件 :XPath Helper
  1、打开 :Ctrl + Shift + X
  2、关闭 :Ctrl + Shift + X
2、Firefox插件 :XPath checker
3、XPath表达式编辑工具 :XML quire

3、xpath匹配规则

1、匹配演示
  1、查找bookstore下所有节点:/bookstore
  2、查找所有的book节点://book
  3、查找所有book下的title节点中,lang属性为"en"的节点
    //book/title[@lang="en"]
  4、查找bookstore下的第2个book节点下的title节点:
    /bookstore/book[2]/title/text()
2、选取节点
  /  : 从根节点开始选取 
  // : 从整个文档中查找节点
       //price  、  /bookstore/book//price
  @  : 选取某个节点的属性
       //title[@lang="en"]
3、 @的使用
  1、选取1个节点 : //title[@lang="en"]
  2、选取N个节点 : //title[@lang]
  3、选取节点的属性值 : //title/@lang
<a class=....,src="http://..."
4、匹配多路径
  1、符号 : |
  2、获取所有book节点下的 title节点和price节点
    //book/title | //book/price
5、函数
  1、contains() : 匹配一个属性值中包含某些字符串的节点
  //title[contains(@lang,"e")]
  2、text() 
    //title[contains(@lang,"e")]/text()

2、lxml库及xpath使用

1、lxml库 :HTML/XML解析库

1、安装 
  python -m pip install lxml
  conda install lxml
2、使用流程
  1、导模块
    from lxml import etree
  2、利用lxml库的etree模块创建解析对象
    parseHtml = etree.HTML(html)
  3、解析对象调用xpath工具定位节点信息
    r_list = parseHtml.xpath('xpath表达式')
### 只要调用了xpath,结果一定是列表 ###
3、示例 :见01_xpath示例.py
4、如何获取节点对象的内容
  节点对象.text
5、案例1 :抓取百度贴吧帖子里面所有的图片
  1、目标 :抓取指定贴吧所有图片
  2、思路
    1、获取贴吧主页URL,下一页:找URL规律
	2、获取1页中每个帖子的URL
	3、对每个帖子URL发请求,获取帖子里图片URL
	4、对图片URL发请求,以wb方式写入本地文件
  3、步骤
    1、获取贴吧主页URL
  http://tieba.baidu.com/f? + 查询参数
2、找到页面中所有帖子的URL
  src : 完整链接
  href : 和主URL进行拼接
    /p/5926064184 千玺:p/6242828073
        http://tieba.baidu.com/p/5926064184
  xpath匹配链接:
   写法1: //div[@class="col2_right j_threadlist_li_right"]/div/div/a/@href

   写法2(推荐): //div[@class="t_con cleafix"]/div/div/div/a/@href
	(好像是错的)千玺://div[@class="d_post_content j_d_post_content  clearfix"]
	微博://div[@box-result clearfix hover]/a/@href
3、找每个帖子中图片URL
  Xpath匹配:
    //img[@class="BDE_Image"]/@src
	
	千玺://ima[@class="left_img"]/@src
4、代码实现
6、案例2 :糗事百科(详情页)-xpath
  1、目标 :用户昵称、段子内容、好笑数、评论数
  2、步骤
    1、找URL
  https://www.qiushibaike.com/8hr/page/1/
2、xpath匹配
  1、基准xpath://div[contains(@id,"qiushi_tag_")]

    用户昵称: ./div/a/h2
    段子内容: .//div[@class="content"]/span
    好笑数量: .//i
    评论数量: .//i
'''02_百度贴吧图片抓取案例.py'''
import requests
from lxml import etree
import time

class BaiduImageSpider:
    def __init__(self):
        self.headers = {"User-Agent":"Mozilla/5.0"}
        self.baseurl = "http://tieba.baidu.com"
        self.pageurl = "http://tieba.baidu.com/f?"
        
    # 获取所有帖子URL列表
    def getPageUrl(self,params):
        res = requests.get(self.pageurl,params=params,headers=self.headers) 
        res.encoding = "utf-8"
        html = res.text
        # 构建解析对象
        parseHtml = etree.HTML(html)
        # 帖子链接列表
        t_list = parseHtml.xpath('//div[@class="t_con cleafix"]/div/div/div/a/@href')
        # t_list : ['/p/233432','/p/2039820',..]
        print(t_list)
        for t_link in t_list:
            # 拼接帖子完整链接
            t_link = self.baseurl + t_link
            self.getImageUrl(t_link)
    
    # 获取帖子中图片URL列表
    def getImageUrl(self,t_link):
        res = requests.get(t_link,headers=self.headers)
        res.encoding = "utf-8"
        html = res.text
        # 构造解析对象
        parseHtml = etree.HTML(html)
        img_list = parseHtml.xpath('//img[@class="BDE_Image"]/@src')
        print(img_list)
        for img_link in img_list:
            self.writeImage(img_link)
    
    # 保存到本地
    def writeImage(self,img_link):
        # 获取图片的bytes
        res = requests.get(img_link,headers=self.headers)
        res.encoding = "utf-8"
        html = res.content
        # filename
        filename = img_link[-12:]
        with open(filename,"wb") as f:
            f.write(html)
            time.sleep(0.5)
            print("%s下载成功" % filename)
    
    # 主函数
    def workOn(self):
        name = input("请输入贴吧名:")
        begin = int(input("请输入起始页:"))
        end = int(input("请输入终止页:"))
        
        for n in range(begin,end+1):
            pn = (n-1)*50
            params = {
                    "kw":name,
                    "pn":str(pn)
                }
            self.getPageUrl(params)
            
if __name__ == "__main__":
    spider = BaiduImageSpider()
    spider.workOn()

这个代码我爬取了千玺的图片,但其中有很多其他的图片,是这样的。
在这里插入图片描述
中间有一些其他的,我也不知道哪来的,在页面上也没显示。
爬取王俊凯的就没有这样,就全是图片
在这里插入图片描述
需要注意的就是抓取的代码需单独保存在一个文件夹里面,不然爬下来的图片会很凌乱。

#03_糗事百科(详情页)-xpath
import requests
from lxml import etree
import pymongo
class QuishiSpider:
    def __init__(self):
        self.url = "https://www.qiushibaike.com/8hr/page/1/"
        self.headers = {"User-Agent":"Mozilla/5.0"}
        self.conn = pymongo.MongoClient("localhost",27017)
        self.db = self.conn.Baikedb
        self.myset = self.db.baikeset
    
    def getPage(self):
        res = requests.get(self.url,headers = self.headers)
        res.encoding = "utf-8"
        html = res.text
        self.parsePage(html)
        
    def parsePage(self,html):
        parseHtml = etree.HTML(html)
        #基准xpath,匹配每个段子的列表
        base_list = parseHtml.xpath('//div[contains(@id,"qiushi_tag_")]')
        
        #遍历每个段子的节点对象(base)
        for base in base_list:
            #用户昵称
            uername = base.xpath('./div/a/h2')
            if len(username) == 0:
                username = "匿名用户"
            else:
                username = username[0].text
                
            #段子内容
            content = base.xpath('.//div[@class="content"]/span')[0].text
            #好笑数量
            #[<element.好笑数量>,<element.评论>]
            laughNum = base.xpath('.//i')[0].text
            
            #评论数量
            pingNum = base.xpath('.//i')[1].text
            
            #定义为字典,存入数据库
            d = {
                    "username":username.strip(),
                    "content":content.strip(),
                    "laughNum":laughNum.strip(),
                    "pingNum":pingNum.strip()
                    }
            self.myset.insert(d)
            pirnt("存入数据库成功")
        
    
if __name__ =="__main__":
    spider = QuishiSpider()
    spider.getPage()

爬虫基础就这样学完了,虽然还有一些值得我无仔细思量的地方,但是时间不等人,只能边学后面的边复习前面的了,加油。

WEBCRAWLER 网络爬虫实训项目 1 WEBCRAWLER 网 络 爬 虫 实 训 项 目 文档版本: 1.0.0.1 编写单位: 达内IT培训集团 C++教学研发部 编写人员: 闵卫 定稿日期: 2015年11月20日 星期五WEBCRAWLER 网络爬虫实训项目 2 1. 项目概述 互联网产品形形色色,有产品导向的,有营销导向的,也有技术导向的,但是 以技术见长的互联网产品比例相对小些。搜索引擎是目前互联网产品中最具技 术含量的产品,如果不是唯一,至少也是其中之一。 经过十几年的发展,搜索引擎已经成为互联网的重要入口之一,Twitter联合创 始人埃文•威廉姆斯提出了“域名已死论”,好记的域名不再重要,因为人们会 通过搜索进入网站。搜索引擎排名对于中小网站流量来说至关重要。了解搜索 引擎简单界面背后的技术原理其实对每一个希望在互联网行业有所建树的信息 技术人员都很重要。 1.1. 搜索引擎 作为互联网应用中最具技术含量的应用之一,优秀的搜索引擎需要复杂的架构 和算法,以此来支撑对海量数据的获取、 存储,以及对用户查询的快速而准确 地响应。 从架构层面,搜索引擎需要能够对以百亿计的海量网页进行获取、 存 储、 处理的能力,同时要保证搜索结果的质量。 如何获取、 存储并计算如此海WEBCRAWLER 网络爬虫实训项目 3 量的数据?如何快速响应用户的查询?如何使得搜索结果尽可能满足用户对信 息的需求?这些都是搜索引擎的设计者不得不面对的技术挑战。 下图展示了一个通用搜索引擎的基本结构。商业级别的搜索引擎通常由很多相 互独立的模块组成,各个模块只负责搜索引擎的一部分功能,相互配合组成完 整的搜索引擎: 搜索引擎的信息源来自于互联网网页,通过“网络爬虫” 将整个“互联网” 的 信息获取到本地,因为互联网页面中有相当大比例的内容是完全相同或者近似 重复的,“网页去重”模块会对此做出检测,并去除重复内容。 在此之后,搜索引擎会对网页进行解析,抽取网页主体内容,以及页面中包含 的指向其它页面的所谓超链接。 为了加快用户查询的响应速度,网页内容通过 “倒排索引”这种高效查询数据结构来保存,而网页之间的链接关系也会予以 保存。之所以要保存链接关系,是因为这种关系在网页相关性排序阶段是可利 用的,通过“链接分析”可以判断页面的相对重要性,对于为用户提供准确的 搜索结果帮助很大。 由于网页数量太多,搜索引擎不仅需要保存网页的原始信息,还要保存一些中 间处理结果,使用单台或者少量的计算机明显是不现实的。 Google等商业搜索 引擎提供商,为此开发了一整套云存储与云计算平台,使用数以万计的普通PCWEBCRAWLER 网络爬虫实训项目 4 搭建了海量信息的可靠存储与计算架构,以此作为搜索引擎及其相关应用的基 础支撑。优秀的云存储与云计算平台已经成为大型商业搜索引擎的核心竞争 力。 以上所述是搜索引擎如何获取并存储海量的网页相关信息。这些功能因为不需 要实时计算,所以可以被看作是搜索引擎的后台计算系统。搜索引擎的首要目 标当然是为用户提供准确而全面的搜索结果,因此响应用户查询并实时提供准 确结果便构成了搜索引擎的前台计算系统。 当搜索引擎接收到用户的查询请求后,首先需要对查询词进行分析,通过与用 户信息的结合,正确推导出用户的真实搜索意图。 此后,先在“Cache系统” 所维护的缓存中查找。搜索引擎的缓存存储了不同的搜索意图及其相对应的搜 索结果。如果在缓存中找到满足用户需求的信息,则直接将搜索结果返回给用 户。这样既省掉了重复计算对资源的消耗,又加快了整个搜索过程的响应速 度。而如果在缓存中没有找到满足用户需求的信息,则需要通过“网页排 序”,根据用户的搜索意图,实时计算哪些网页是满足用户需求的,并排序输 出作为搜索结果。 而网页排序最重要的两个参考因素,一个是“内容相似 性”,即哪些网页是和用户的搜索意图密切相关的;一个是网页重要性,即哪 些网页是质量较好或相对重要的,而这往往可以从“链接分析”的结果中获 得。综合以上两种考虑,前台系统对网页进行排序,作为搜索的最终结果。 除了上述功能模块,搜索引擎的“反作弊”模块近年来越来越受到重视。搜索 引擎作为互联网用户上网的入口,对于网络流量的引导和分流至关重要,甚至 可以说起着决定性的作用。因此,各种“作弊”方式也逐渐流行起来,通过各 种手段将网页的搜索排名提前到与其网页质量不相称的位置,这会严重影响用 户的搜索体验。所以,如何自动发现作弊网页并对其给于相应的惩罚,就成了 搜索引擎非常重要的功能之一。 1.2. 网络爬虫 通用搜索引擎的处理对象是互联网网页,截至目前的网页数量数以百万计,所 以搜索引擎首先面临的问题就是如何能够设计出高效的下载系统,将如此海量 的网页数据传送到本地,在本地形成互联网网页的镜像备份。 网络爬虫即扮演 如此角色。 它是搜索引擎中及其关键的基础构件。WEBCRAWLER 网络爬虫实训项目 5 网络爬虫的一般工作原理如下图所示:  从互联网网页中选择部分网页的链接作为“种子URL”,放入“待抓取URL 队列”;  爬虫从“待抓取URL队列”中依次“读取URL”;  爬虫通过“DNS解析” 将读到的URL转换为网站服务器的IP地址;  爬虫将网站服务器的IP地址、通信端口、网页路径等信息交给“网页下载” 器;  “网页下载”器负责从“互联网”上下载网页内容;  对于已经下载到本地的网页内容,一方面将其存储到“下载页面库” 中,等 待建立索引等后续处理,另一方面将其URL放入“已抓取URL队列”,后者显 然是为了避免网页被重复抓取;  对于刚刚下载到本地的网页内容,还需要从中“抽取URL”;  在“已抓取URL队列”中检查所抽取的URL是否已被抓取过;  如果所抽取的URL没有被抓取过,则将其排入“待抓取URL队列” 末尾,在 之后的抓取调度中重复第步,下载这个URL所对应的网页。 如此这般,形成WEBCRAWLER 网络爬虫实训项目 6 循环,直到“待抓取URL队列”空,这表示爬虫已将所有能够被抓取的网页尽 数抓完,完成一轮完整的抓取过程。 以上所述仅仅是网络爬虫的一般性原理,具体实现过程中还可以有很多优化的 空间,比如将“网页下载”以多线索(进程或线程)并发的方式实现,甚至将 “DNS解析”也处理为并发的过程,以避免爬虫系统的I/O吞吐率受到网站服 务器和域名解析服务器的限制。而对于“已抓取URL队列”则可以采用布隆排 重表的方式加以优化,以降低其时间和空间复杂度。 2. 总体架构 本项目总体架构如下图所示: 配置器 Configurator 超文本传输协议响应 HttpResponse 日志 Log 主线程 main 多路输入输出 MultiIo 插件管理器 PluginMngr 套接字 Socket 字符串工具包 StrKit 统一资源定位符队列 UrlQueues 网络爬虫 WebCrawler 原始统一资源定位符 RawUrl 超文本传输协议响应包头 HttpHeader 域名解析线程 DnsThread 解析统一资源定位符 DnsUrl 接收线程 RecvThread 布隆过滤器 BloomFilter 哈希器 Hash 最大深度插件 MaxDepth 域名限制插件 DomainLimit 超文本传输协议响应包头过滤器插件 HeaderFilter 超文本标记语言文件存储插件 SaveHTMLToFile 图像文件存储插件 SaveImageToFile 发送线程 SendThreadWEBCRAWLER 网络爬虫实训项目 7 2.1. 基础设施 2.1.1. 字符串工具包(StrKit) 常用字符串处理函数。 2.1.2. 日志(Log) 分等级,带格式的日志文件打印。 2.1.3. 配置器(Configurator) 从指定的配置文件中加载配置信息。 2.1.4. 多路输入输出(MultiIo) 封装epoll多路I/O系统调用,提供增加、删除和等待操作接口。 2.1.5. 插件管理器(PluginMngr) 加载插件并接受其注册,维护插件对象容器并提供调用其处理函数的外部接 口。 2.2. 网络通信 2.2.1. 哈希器(Hash) 封装各种哈希算法函数。 2.2.2. 布隆过滤器(BloomFilter) 基于布隆算法,对欲加入队列的原始统一资源定位符进行过滤,以防止已被抓 取过的URL再次入队,降低冗余开销同时避免无限循环。 2.2.3. 原始统一资源定位符(RawUrl) 提供原始形态的统一资源定位符字符串的简单包装,以及规格化等辅助支持。 2.2.4. 解析统一资源定位符(DnsUrl) 将原始形态的统一资源定位符字符串,解析为服务器域名、资源路径、服务器 IP地址,乃至服务器通信端口等。WEBCRAWLER 网络爬虫实训项目 8 2.2.5. 统一资源定位符队列(UrlQueues) 封装原始统一资源定位符队列和解析统一资源定位符队列,提供线程安全的入 队、出队操作,通过统一资源定位符过滤器排重,同时支持基于正则表达式的 统一资源定位符抽取功能。 2.2.6. 套接字(Socket) 发送/接收超文本传输协议请求/响应,发送成功将套接字描述符加入多路I/O, 接收成功抽取统一资源定位符压入队列。 2.2.7. 超文本传输协议响应包头(HttpHeader) 状态码和内容类型等关键信息。 2.2.8. 超文本传输协议响应(HttpResponse) 服务器统一资源定位符和超文本传输协议包头、包体及长度的简单封装。 2.3. 流程控制 2.3.1. 域名解析线程(DnsThread) 从原始统一资源定位符队列中弹出RawUrl对象,借助域名解析系统(DNS)获 取服务器的IP地址,构造DnsUrl对象压入解析统一资源定位符队列。 2.3.2. 发送线程(SendThread) 通过WebCrawler对象启动新的抓取任务,从解析统一资源定位符队列中弹出 DnsUrl对象,向HTTP服务器发送HTTP请求,并将套接字描述符放入MultiIo 对象。 2.3.3. 接收线程(RecvThread) 由WebCrawler对象在从MultiIo对象中等到套接字描述符可读时动态创建,通 过Socket对象接收超文本传输协议响应。WEBCRAWLER 网络爬虫实训项目 9 2.3.4. 网络爬虫(WebCrawler) 代表整个应用程序的逻辑对象,构建并维护包括日志、配置器、多路I/O、插件 管理器、统一资源定位符队列、域名解析线程等在内的多个底层设施,提供诸 如初始化、执行多路输入输出循环、启动抓取任务等外部接口。 2.3.5. 主线程(main) 主函数,处理命令行参数,初始化应用程序对象,进入多路I/O循环。 2.4. 外围扩展 2.4.1. 最大深度插件(MaxDepth) 根据配置文件的MAX_DEPTH配置项,对被抓取超链接的最大递归深度进行限 制。 2.4.2. 域名限制插件(DomainLimit) 根据配置文件的INCLUDE_PREFIXES和EXCLUDE_PREFIXES配置项,对被抓取 超链接的前缀进行限制。 2.4.3. 超文本传输协议响应包头过滤器插件(HeaderFilter) 根据配置文件的ACCEPT_TYPE配置项,对超文本传输协议响应的内容类型进行 限制。 2.4.4. 超文本标记语言文件存储插件(SaveHTMLToFile) 将用超文本标记语言描述的页面内容保存到磁盘文件中。 2.4.5. 图像文件存储插件(SaveImageToFile) 将页面内容中引用的图像资源保存到磁盘文件中。 3. 工作流程 3.1. 主事件流 进程入口函数在进行必要的命令行参数处理和系统初始化以后,进入网络爬虫 的多路输入输出循环,一旦发现某个与服务器相连的套接字有数据可读,即创WEBCRAWLER 网络爬虫实训项目 10 建接收线程,后者负责抓取页面内容,而前者继续于多路输入输出循环中等待 其它套接字上的I/O事件。 3.2. 解析事件流 独立的域名解析线程实时监视原始统一资源定位符队列的变化,并将其中的每 一条新近加入的原始统一资源定位符,借助域名解析系统转换为解析统一资源 定位符,并压入解析统一资源定位符队列。 3.3. 发送事件流 不断从解析统一资源定位符队列弹出解析统一资源定位符,创建套接字,根据 服务器的IP地址和通信端口发起连接请求,建立TCP连接,发送超文本传输协 议请求包,并将套接字放入多路输入输出对象,由主事件流等待其数据到达事 件。 3.4. 接收事件流 每个超文本传输线程通过已明确有数据可读的套接字接收来自服务器的超文本 传输协议响应,并交由统一资源定位符队列进行超链接抽取和布隆排重过滤, 直至压入原始统一资源定位符队列。在压入原始统一资源定位符队列之前,以 及接收到超文本传输协议包头和包体之后,分别执行统一资源定位符插件、超 文本传输协议包头插件和超文本标记语言插件的处理过程。 以上四个事件流,需要平行且独立地并发运行,并在共享资源和执行步调上保 持适度的同步。 4. 目录结构 本项目的目录结构如下所示: WebCrawler/ ├── bin/ │ ├── WebCrawler │ ├── WebCrawler.cfg │ └── WebCrawler.scr ├── docs/ │ ├── 概要设计.pdfWEBCRAWLER 网络爬虫实训项目 11 │ └── 详细设计.pdf ├── download/ ├── plugins/ │ ├── DomainLimit.cpp │ ├── DomainLimit.h │ ├── DomainLimit.mak │ ├── DomainLimit.so │ ├── HeaderFilter.cpp │ ├── HeaderFilter.h │ ├── HeaderFilter.mak │ ├── HeaderFilter.so │ ├── MaxDepth.cpp │ ├── MaxDepth.h │ ├── MaxDepth.mak │ ├── MaxDepth.so │ ├── SaveHTMLToFile.cpp │ ├── SaveHTMLToFile.h │ ├── SaveHTMLToFile.mak │ ├── SaveHTMLToFile.so │ ├── SaveImageToFile.cpp │ ├── SaveImageToFile.h │ ├── SaveImageToFile.mak │ ├── SaveImageToFile.so │ └── mkall └── src/ ├── BloomFilter.cpp ├── BloomFilter.h ├── Configurator.cpp ├── Configurator.h ├── DnsThread.cpp ├── DnsThread.h ├── Hash.cpp ├── Hash.h ├── Http.h ├── Log.cpp ├── Log.h ├── Main.cpp ├── Makefile ├── MultiIo.cpp ├── MultiIo.h ├── Plugin.h ├── PluginMngr.cpp ├── PluginMngr.h ├── Precompile.h ├── RecvThread.cpp ├── RecvThread.h ├── SendThread.cpp ├── SendThread.h ├── Socket.cpp ├── Socket.hWEBCRAWLER 网络爬虫实训项目 12 ├── StrKit.cpp ├── StrKit.h ├── Thread.cpp ├── Thread.h ├── Url.cpp ├── Url.h ├── UrlFilter.h ├── UrlQueues.cpp ├── UrlQueues.h ├── WebCrawler.cpp └── WebCrawler.h 其中bin目录存放可执行程序文件、启动画面文件和配置文件,docs目录存放 项目文档,download目录存放爬虫下载的网页文件和图像文件,plugins目录 存放扩展插件的源代码和共享库文件,src目录存放项目主体部分的源代码文 件。 在教学环境下,以上目录结构可分别放在teacher和student两个子目录中。其 中teacher目录包含完整的程序源码和资料文档,以为学生开发时提供参考和借 鉴。 student目录中的源代码是不完整的,部分类或者函数的实现只给出了基 本框架,但代码中的注释和teacher目录下对应的部分完全相同,其中缺失的内 容,需要学生在理解整体设计思路和上下文逻辑的前提下予以补全。需要学生 参与补全的源代码文件详见开发计划。 5. 开发计划 本项目拟在四个工作日内完成: 工作日 模块 子模块 代码文件 第一天 基础设施 预编译头 Precompile Precompile.h 字符串工具包 StrKit StrKit.h StrKit.cpp 日志 Log Log.h Log.cpp 配置器 Configurator Configurator.h Configurator.cppWEBCRAWLER 网络爬虫实训项目 13 多路输入输出 MultiIo MultiIo.h MultiIo.cpp 插件接口 Plugin Plugin.h 插件管理器 PluginMngr PluginMngr.h PluginMngr.cpp 第二天 网络通信 哈希器 Hash Hash.h Hash.cpp 统一资源定位 符过滤器接口 UrlFilter UrlFilter.h 布隆过滤器 BloomFilter BloomFilter.h BloomFilter.cpp 原始统一资源定位符 RawUrl Url.h 解析统一资源定位符 DnsUrl Url.cpp 统一资源定位符队列 UrlQueues UrlQueues.h UrlQueues.cpp 套接字 Socket Socket.h Socket.cpp 超文本传输协 议响应包头 HttpHeader Http.h 超文本传输协议响应 HttpResponse 第三天 流程控制 线程 Thread Thread.h Thread.cpp 域名解析线程 DnsThread DnsThread.h DnsThread.cpp 发送线程 SendThread SendThread.h SendThread.cppWEBCRAWLER 网络爬虫实训项目 14 接收线程 RecvThread RecvThread.h RecvThread.cpp 网络爬虫 WebCrawler WebCrawler.h WebCrawler.cpp 主线程 main Main.cpp 构建脚本 Makefile Makefile 第四天 外围扩展 最大深度插件 MaxDepth MaxDepth.h MaxDepth.cpp MaxDepth.mak 域名限制插件 DomainLimit DomainLimit.h DomainLimit.cpp DomainLimit.mak 超文本传输协议响 应包头过滤器插件 HeaderFilter HeaderFilter.h HeaderFilter.cpp HeaderFilter.mak 超文本标记语言 文件存储插件 SaveHTMLToFile SaveHTMLToFile.h SaveHTMLToFile.cpp SaveHTMLToFile.mak 图像文件存储插件 SaveImageToFile SaveImageToFile.h SaveImageToFile.cpp SaveImageToFile.cpp 构建脚本 mkall mkall 其中被突出显示的代码文件中,包含需要学生添加的内容,注意源文件中形 如“// 此处添加代码”的注释。WEBCRAWLER 网络爬虫实训项目 15 6. 知识扩展 为了能在实训环节,进一步强化学生独立思考、独立解决问题的能力,本项目 有意涵盖了一些前期课程中不曾涉及或只作为一般性了解的知识和技巧。具体 包括:  预编译头文件  std::string  变长参数表  基于epoll的多路I/O  哈希算法和布隆表  URL、 DNS、 HTTP和HTML  正则表达式  线程封装  精灵进程和I/O重定向  Makefile 对于上述内容,建议项目指导教师根据学生的接受能力,结合项目中的具体应 用,在项目正式启动之前,先做概要性介绍,同时提供进一步详细学习和研究 的线索,包括man手册、参考书、网络链接或其它媒体资源,尽量让学生通过 自己的实践和探索找到解决问题的方法,这才是项目实训的意义所在!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值