《精通python网络爬虫》笔记
《精通python网络爬虫》韦玮 著
目录结构
第一章 什么是网络爬虫
第二章 爬虫技能概览
第三章 爬虫实现原理与实现技术
第四章 Urllib库与URLError异常处理
第五章 正则表达式与Cookie使用
第六章 手写Python爬虫
第七章 学会使用 Fiddler
第八章 爬虫的浏览器伪装技术
第九章 爬虫的定向爬取技术
第十章 了解Python爬虫框架
第十一章 爬虫利器----Scrapy安装与配置
第十二章 开启Scrapy爬虫项目之旅
第十三章 Scrapy核心架构
第十四章 Scrapy 中文输出与存储
第十五章 编写自动爬取网页的爬虫
第十六章 CrawlSpider
第十七章 Scrapy高级应用
第十八章 博客类爬虫项目
第十九章 图片类爬虫项目
第二十章 模拟登陆爬虫项目
Urllib库的使用
在Python2.x中有Urllib库也有Urllib2库,在Python3.x中Urllib2合并到Urllib中。本书使用的是python3.5.2
爬取网页
import urllib.request
file = urllib.request.urlopen("http://www.cnblogs.com/0bug/")
data = file.read() # 读取全部内容赋给一个字符串变量
# datalines = file.readlines() # 与read不同的是把读取到的内容赋值给列表变量
# dataline = file.readline() # 读取文件的一行内容
print(data)
将爬取的内容以网页的形式保存到本地
import urllib.request
file = urllib.request.urlopen("http://www.cnblogs.com/0bug/")
data = file.read()
with open(r'D:\1.html', "wb") as f:
f.write(data)
除此之外,还可以使用urllib.request里面的urlretrieve()函数直接将对应信息写入本地
import urllib.request
file = urllib.request.urlretrieve("http://www.cnblogs.com/0bug/", filename=r"D:\2.html")
urllib.request.urlcleanup() # 清除缓存
将url编码
一般来说,url标准中只允许一部分的ASCII字符比如数字、字母、部分符号等,而其他的比如汉子不符合标准需要进行编码
import urllib.request
r = urllib.request.quote('http://www.cnblogs.com/0bug/') # 编码
print(r) # http%3A//www.cnblogs.com/0bug/
r2 = urllib.request.unquote('http%3A//www.cnblogs.com/0bug/') # 解码
print(r2) # http://www.cnblogs.com/0bug/
添加报头两种方式
方式一
import urllib.request
url = 'http://www.cnblogs.com/0bug/'
headers = ("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
opener = urllib.request.build_opener()
opener.addheaders = [headers]
data = opener.open(url).read()
with open(r'D:\3.html', "wb") as f:
f.write(data)
方式二
import urllib.request
url = 'http://www.cnblogs.com/0bug/'
req = urllib.request.Request(url)
req.add_header("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
data = urllib.request.urlopen(req).read()
with open(r'D:\4.html', "wb") as f:
f.write(data)
超时设置
import urllib.request
url = 'http://www.cnblogs.com/0bug/'
for i in range(100):
try:
file = urllib.request.urlopen(url, timeout=0.6) # 单位是秒
data = file.read()
print(len(data))
except Exception as e:
print('出现异常-->' + str(e))
构造GET请求
(注意:需要通过urllib.request.quote()对URL进行编码处理)
import urllib.request
keywd = '春江花月夜'
keywd=urllib.request.quote(keywd)
url = "https://www.baidu.com/s?wd=" + keywd
req = urllib.request.Request(url)
req.add_header("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
data = urllib.request.urlopen(req).read()
with open(r'D:\5.html', "wb") as f:
f.write(data)
构造POST请求
不考虑Cookie,以作者提供的这个网站为例http://www.iqianyue.com/mypost
import urllib.request
import urllib.parse
url = 'http://www.iqianyue.com/mypost'
postdata = urllib.parse.urlencode({
'name': 'lichengguang',
'pass': 'lcg*^_8'
}).encode('utf-8') # 将数据使用urlencode编码处理后,使用encode设置为utf-8编码
req = urllib.request.Request(url, postdata)
req.add_header("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
data = urllib.request.urlopen(req).read()
with open(r'D:\6.html', "wb") as f:
f.write(data)
代理服务的设置
import urllib.request
def use_proxy(proxy_addr, url):
"""
该函数实现使用代理服务器来爬取某个URL网页的功能
:param proxy_addr: 代理服务器的地址
:param url: 爬取的网页的地址
:return: 爬取的网页内容
"""
proxy = urllib.request.ProxyHandler({'http': proxy_addr})
opener = urllib.request.build_opener(proxy, urllib.request.HTTPHandler)
urllib.request.install_opener(opener) # 创建全局默认的opener对象,那么使用urlopen()时亦会使用我们安装的opener对象
data = urllib.request.urlopen(url).read().decode("utf-8")
return data
proxy_addr = '114.228.210.103:8118' # 代理地址取自:http://www.xicidaili.com/
data = use_proxy(proxy_addr, 'http://www.cnblogs.com/0bug/')
print(len(data)) # 如果proxy_addr失效,则会抛出异常
开启DebugLog
有时我们希望边运行程序边打印调试日志,此时就需要开启DebugLog。
开启步骤:
1、分别使用 urllib. request Httphandler(和 urllib. request. Httpshandlero将 debuglevel设置为1
2、使用 urllib request build opener0创建自定义的 opener对象,并使用1)中设置的值作为参数。
3、用 urllib request install opener0创建全局默认的 opener对象,这样,在使用urlopeno时,也会使用我们安装的 opener对象。
4、进行后续相应的操作,比如 urlopeno等。此时,根据以上思路,我们可以通过如下代码开启 Debug
import urllib.request
httphd = urllib.request.HTTPHandler(debuglevel=1)
httpshd = urllib.request.HTTPSHandler(debuglevel=1)
opener = urllib.request.build_opener(httphd, httpshd)
urllib.request.install_opener(opener)
data = urllib.request.urlopen("http://edu.51cto.com")
print(data)
异常处理神器-URLError
URLError:
import urllib.request
import urllib.error
"""
URLError原因:
1.连接不上服务器
2.远程URL不存在
3.无网络
4.触发了HTTPError
"""
try:
data = urllib.request.urlopen("http://www.adafsdagrfesda.com")
print(data.read())
except urllib.error.URLError as e:
print(e.reason)
HTTPError
HTTPError为URLError的子类,这种方法只能处理以上四中情况中的第四种情况
import urllib.request
import urllib.error
try:
data = urllib.request.urlopen("http://www.fsadwafddss.com")
print(data.encode)
except urllib.error.HTTPError as e:
print(e.code)
print(e.reason)
如果我们需要在抛出HTTPError异常的时候得到e.code就不能用方法一里的URLError那段代码代替HTTPError,但是可以做一下改进。使用hasattr函数判断是否有code属性。
代码改进如下:
import urllib.request
import urllib.error
try:
data = urllib.request.urlopen('http://wwwmm.cnblogs.com/0bug/').read()
print(data)
except urllib.error.URLError as e:
if hasattr(e, "code"):
print(e.code)
if hasattr(e, "reason"):
print(e.reason)
# 404
# Not Found
常见异常码:
200 OK
一切正常
300 Moved Permanently
重定向到新的URL,永久性
302 Found
重定向到临时的URL,非永久性
304 Not Modified
请求的资源未更新
400 Bad Request
非法请求
401 Unauthorized
请求未经授权
403 Forbidden
禁止访问
404 Not Found
没有找到对应的页面
500 Internal Server Error
服务器内部出现错误
501 Not Implemented
服务器不支持实现请求所需的功能
正则表达式
这里有过总结:http://www.cnblogs.com/0bug/p/8262662.html
- 普通字符作为原子(数字,大小写字母,下划线)
import re
pattern = '0bug'
str_url = 'http://www.cnblogs.com/0bug/'
r = re.search(pattern, str_url)
print(type(r)) # <class '_sre.SRE_Match'>
print(r) # <_sre.SRE_Match object; span=(23, 27), match='0bug'>
print(r[0]) # 0bug
print(r.group()) # 0bug
- 非打印字符作为原子(指的是在字符串中用于格式控制的字符,如换行符等)
\n
:用于匹配一个换号符
\t
:用于匹配一个制表符
import re
pattern = '\n'
string = '''aaaaa
bbbbbb'''
result = re.search(pattern, string)
print(result)
# <_sre.SRE_Match object; span=(5, 6), match='\n'>
- 通用子符作为原子(一个原子可以匹配一类字符)
\w:匹配任意数字、字母或下划线
\W:匹配除数字、字母或下划线以外的任意一个字符
\d:匹配任意一个十进制数
\D:匹配除十进制数以外的任意一其他字符
\s:匹配任意一个空白字符
\S:匹配除空白字符以外的任意一其他字符
例子:
import re
pattern = '\w\dpython\w'
string = 'abcdf233pythonadad_'
result = re.search(pattern, string)
print(result)
# <_sre.SRE_Match object; span=(6, 15), match='33pythona'>
- 原子表 []
原子表可以定义一组地位平等的原子,然后匹配 的时候回取该原子表中的任意一个原子进行匹配。
import re
pattern = 'a[bcd]'
string = 'abdsacdaadfsfaaae_'
result = re.compile(pattern).findall(string)
print(result)
# ['ab', 'ac', 'ad']
- 原字符(正则表达式中具有一些特殊含义的字符)
.: 匹配除换行符以外的任意字符
^: 匹配字符串的开始位置
$: 匹配字符串的结束位置
*: 匹配0次1次或多次前面的原子
?: 匹配0次或1次前面的原子
+: 匹配1次或多次前面的原子
{n}: 前面的原子恰巧出现n次
{n,}: 前面的原子至少出现n次
{n,m}: 前面的原子至少出现n次,最多出现m次
|: 模式选择符
(): 模式单元符
模式选择符:|
使用模式选择符可以设置多个模式,匹配时,可以从中选择任意一个模式匹配,
import re
pattern = 'ab\w|cd\w'
string = 'abdsacdaabfsfaaae_'
result = re.compile(pattern).findall(string)
print(result)
# ['abd', 'cda', 'abf']
模式单元符()
可以使用模式单元符“()”将一些原子组合一个大原子使用,小括号括起来的部分会被当做一个整体去使用。
import re
pattern1 = '(as){2}'
pattern2 = '(ab){2}'
string = 'asababdsfdcdab'
result1 = re.compile(pattern1).findall(string)
result2 = re.compile(pattern2).findall(string)
print(result1) # []
print(result2) # ['ab']
模式修正符
所谓模式修正符,即在不改变正则表达式的情况下,通过模式修正符改变正则表达式的含义,从而实现一些匹配结果的调整等功能。
I:匹配时忽略大小写
M:多行匹配
L:做本地化识别匹配
U:根据Unicode字符及解析字符
S:让.匹配包括换行符,即用了该模式修正符后,'.'匹配就可以匹配任意的字符了
- 贪婪模式与懒惰模式
总的来说,贪婪模式就行尽可能多的匹配,相反地,懒惰匹配是尽可能少的匹配。
import re
pattern1 = 'a.*b' # 贪婪匹配
pattern2 = 'a.*?b' # 懒惰匹配
string = 'asababdsfdcdab'
result1 = re.compile(pattern1).findall(string)
result2 = re.compile(pattern2).findall(string)
print(result1) # ['asababdsfdcdab']
print(result2) # ['asab', 'ab', 'ab']
- re模块常见函数
re.match()函数、re.search()函数、全局匹配函数、re.sub()函数。
re.match()函数
re.match(pattern, string, flags=0)
功能:从源字符串的起始匹配
参数说明:
- pattern: 正则表达式
- string: 对应的源字符
- flags: 可选,代表对应的标志位,可以放模式修正符
re.search()函数
功能:与re.match()函数不同的是re.search()函数会扫描整个字符串并进行对应的匹配。
全局匹配函数
re.match()函数与re.search()函数匹配的结果中,即便是源字符串中有多个结果符合模式,也只会匹配到一个结果,全局匹配可以匹配到所有符合匹配模式的结果。
思路:
1. 使用re.complie()对正则表达式进行预编译。
2. 编译后,使用findall()根据正则表达式从源字符中将匹配的结果全部找出。
以下实现以便理解:
import re
string = 'asdaabcsdabcdssabccab'
pattern = re.compile(".abc.")
result = pattern.findall(string)
print(result) # ['aabcs', 'dabcd', 'sabcc']
上面的实例可以改写成:
import re
string = 'asdaabcsdabcdssabccab'
pattern = ".abc."
result = re.compile(pattern).findall(string)
print(result) # ['aabcs', 'dabcd', 'sabcc']
re.sub函数
sub(pattern, repl, string, count=0, flags=0)
功能:实现替换匹配到的字符串
参数说明:
- pattern:正则表达式
- repl: 要替换成的字符串
- string 源字符串
- count 可选项,代表最多替换次数,如果忽略不写,会将符合模式的结果全部替换
- flags 可选,代表对应的标志位,可以放模式修正符
例子:
import re
string = 'asdaabcsdabcdssabccab'
pattern1 = ".abc."
pattern2 = "s"
result1 = re.sub(pattern1, "xxx", string)
result2 = re.sub(pattern2, "6", string, count=2)
print(result1) # asdxxxxxxsxxxab
print(result2) # a6daabc6dabcdssabccab
Cookiejar实战精析(无csrf_coken)
import urllib.request
import urllib.parse
import http.cookiejar
import re
url = 'http://bbs.chinaunix.net/member.php?mod=logging&action=login&loginsubmit=yes&loginhash=LiZ3D'
postdata = urllib.parse.urlencode({
'username': 'weisuen',
'password': 'aA123456'
}).encode('utf-8')
req = urllib.request.Request(url, postdata)
req.add_header("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
# 使用http.cookiejar.CookieJar()创建CookieJar对象
cjar = http.cookiejar.CookieJar()
# 使用HTTPCookieProcessor创建cookie,并以其为参数构建opener对象
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cjar))
# 将opener安装为全局
urllib.request.install_opener(opener)
file = opener.open(req)
data = file.read()
with open(r'D:\7.html', "wb") as f1:
f1.write(data)
url2 = 'http://bbs.chinaunix.net/'
data2 = urllib.request.urlopen(url2).read()
with open(r'D:\8.html', "wb") as f2:
f2.write(data2)
图片爬虫实战
爬取京东手机图https://list.jd.com/list.html?cat=9987,653,655&page=1
"""
图片1对应代码:
<img width="220" height="220" data-img="1" data-lazy-img="//img12.360buyimg.com/n7/jfs/t2437/118/775474476/74776/4087862f/562616d9Nc17cd80a.jpg">
图片2对应代码:
<img width="220" height="220" data-img="1" data-lazy-img="//img10.360buyimg.com/n7/jfs/t2230/83/2893465811/162158/80a547ef/56fa0f30N7794db4a.jpg">
"""
import re
import urllib.request
import urllib.error
def craw(url, page):
html1 = urllib.request.urlopen(url).read()
html1 = str(html1)
pat1 = '<div id="plist".+? <div class="page clearfix">'
result1 = re.compile(pat1).findall(html1)
result1 = result1[0]
pat2 = '<img width="220" height="220" data-img="1" data-lazy-img="//(.+?\.jpg)">'
imagelist = re.compile(pat2).findall(result1) # 将不需要的过滤掉
x = 1
for imageurl in imagelist:
imagename = "D:/" + str(page) + str(x) + ".jpg" # 拼接图片路径和图片名
imageurl = "http://" + imageurl
try:
urllib.request.urlretrieve(imageurl, filename=imagename)
except urllib.error.URLError as e:
if hasattr(e, "code"):
x += 1
if hasattr(e, "reason"):
x += 1
x += 1
for i in range(1, 30):
url = 'https://list.jd.com/list.html?cat=9987,653,655&page=' + str(i) # 每一页的url
craw(url, i)
链接爬虫实战
import re
import urllib.request
def getlink(url):
#模拟成浏览器
headers=("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0")
opener = urllib.request.build_opener()
opener.addheaders = [headers]
#将opener安装为全局
urllib.request.install_opener(opener)
file=urllib.request.urlopen(url)
data=str(file.read())
#根据需求构建好链接表达式
pat='(https?://[^\s)";]+\.(\w|/)*)'
link=re.compile(pat).findall(data)
#去除重复元素
link=list(set(link))
return link
#要爬取的网页链接
url="http://blog.csdn.net/"
#获取对应网页中包含的链接地址
linklist=getlink(url)
#通过for循环分别遍历输出获取到的链接地址到屏幕上
for link in linklist:
print(link[0])
糗事百科爬虫实战
import urllib.request
import re
def getcontent(url, page):
# 模拟成浏览器
headers = ("User-Agent",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0")
opener = urllib.request.build_opener()
opener.addheaders = [headers]
# 将opener安装为全局
urllib.request.install_opener(opener)
data = urllib.request.urlopen(url).read().decode("utf-8")
# 构建对应用户提取的正则表达式
userpat = 'target="_blank" title="(.*?)">'
# 构建段子内容提取的正则表达式
contentpat = '<div class="content">(.*?)</div>'
# 寻找出所有的用户
userlist = re.compile(userpat, re.S).findall(data)
# 寻找出所有的内容
contentlist = re.compile(contentpat, re.S).findall(data)
x = 1
# 通过for循环遍历段子内容并将内容分别赋给对应的变量
for content in contentlist:
content = content.replace("\n", "")
# 用字符串作为变量名,先将对应字符串赋给一个变量
name = "content" + str(x)
# 通过exec()函数实现用字符串作为变量名并赋值
exec(name + '=content')
x += 1
y = 1
# 通过for循环遍历用户,并输出该用户对应的内容
for user in userlist:
name = "content" + str(y)
print("用户" + str(page) + str(y) + "是:" + user)
print("内容是:")
exec("print(" + name + ")")
print("\n")
y += 1
# 分别获取各页的段子,通过for循环可以获取多页
for i in range(1, 30):
url = "http://www.qiushibaike.com/8hr/page/" + str(i)
getcontent(url, i)
微信爬虫实战
搜索物联,根据分析以下链接是有用的:
http://weixin.sogou.com/weixin?query=%E7%89%A9%E8%81%94&type=2&page=9
type控制搜索类型,query对应关键字,page对应页码,我们可以应用前面所学的解码规则解码看一下:
import urllib.request
r2 = urllib.request.unquote('http://weixin.sogou.com/weixin?query=%E7%89%A9%E8%81%94&type=2&page=9') # 解码
print(r2) # http://weixin.sogou.com/weixin?query=物联&type=2&page=9
项目规划:
建立三个自定义函数,一个函数实现使用代理服务器爬取指定网站并返回爬取到的数据的功能,一个函数实现获取多个页面的所有文章链接的功能,另一个函数实现根据文章链接爬取指定标题和内容并写入文件中的功能。需要注意的是,需要对可能会发生异常的地方做异常处理和延迟处理,对关键字使用urllib.request.quote(key)进行编码,完整代码如下
示例代码:
import re
import urllib.request
import time
import urllib.error
# 模拟成浏览器
headers = ("User-Agent",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0")
opener = urllib.request.build_opener()
opener.addheaders = [headers]
# 将opener安装为全局
urllib.request.install_opener(opener)
# 设置一个列表listurl存储文章网址列表
listurl = []
# 自定义函数,功能为使用代理服务器
def use_proxy(proxy_addr, url):
# 建立异常处理机制
try:
import urllib.request
proxy = urllib.request.ProxyHandler({'http': proxy_addr})
opener = urllib.request.build_opener(proxy, urllib.request.HTTPHandler)
urllib.request.install_opener(opener)
data = urllib.request.urlopen(url).read().decode('utf-8')
return data
except urllib.error.URLError as e:
if hasattr(e, "code"):
print(e.code)
if hasattr(e, "reason"):
print(e.reason)
# 若为URLError异常,延时10秒执行
time.sleep(10)
except Exception as e:
print("exception:" + str(e))
# 若为Exception异常,延时1秒执行
time.sleep(1)
# 获取所有文章链接
def getlisturl(key, pagestart, pageend, proxy):
try:
page = pagestart
# 编码关键词key
keycode = urllib.request.quote(key)
# 编码"&page"
pagecode = urllib.request.quote("&page")
# 循环爬取各页的文章链接
for page in range(pagestart, pageend + 1):
# 分别构建各页的url链接,每次循环构建一次
url = "http://weixin.sogou.com/weixin?type=2&query=" + keycode + pagecode + str(page)
# 用代理服务器爬,解决IP被封杀问题
data1 = use_proxy(proxy, url)
# 获取文章链接的正则表达式
listurlpat = '<div class="txt-box">.*?(http://.*?)"'
# 获取每页的所有文章链接并添加到列表listurl中
listurl.append(re.compile(listurlpat, re.S).findall(data1))
print("共获取到" + str(len(listurl)) + "页") # 便于调试
return listurl
except urllib.error.URLError as e:
if hasattr(e, "code"):
print(e.code)
if hasattr(e, "reason"):
print(e.reason)
# 若为URLError异常,延时10秒执行
time.sleep(10)
except Exception as e:
print("exception:" + str(e))
# 若为Exception异常,延时1秒执行
time.sleep(1)
# 通过文章链接获取对应内容
def getcontent(listurl, proxy):
i = 0
# 设置本地文件中的开始html编码
html1 = '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>微信文章页面</title>
</head>
<body>'''
fh = open("D:/9.html", "wb")
fh.write(html1.encode("utf-8"))
fh.close()
# 再次以追加写入的方式打开文件,以写入对应文章内容
fh = open("D:/Python35/myweb/part6/1.html", "ab")
# 此时listurl为二维列表,形如listurl[][],第一维存储的信息跟第几页相关,第二维存的跟该页第几个文章链接相关
for i in range(0, len(listurl)):
for j in range(0, len(listurl[i])):
try:
url = listurl[i][j]
# 处理成真实url,读者亦可以观察对应网址的关系自行分析,采集网址比真实网址多了一串amp
url = url.replace("amp;", "")
# 使用代理去爬取对应网址的内容
data = use_proxy(proxy, url)
# 文章标题正则表达式
titlepat = "<title>(.*?)</title>"
# 文章内容正则表达式
contentpat = 'id="js_content">(.*?)id="js_sg_bar"'
# 通过对应正则表达式找到标题并赋给列表title
title = re.compile(titlepat).findall(data)
# 通过对应正则表达式找到内容并赋给列表content
content = re.compile(contentpat, re.S).findall(data)
# 初始化标题与内容
thistitle = "此次没有获取到"
thiscontent = "此次没有获取到"
# 如果标题列表不为空,说明找到了标题,取列表第零个元素,即此次标题赋给变量thistitle
if (title != []):
thistitle = title[0]
if (content != []):
thiscontent = content[0]
# 将标题与内容汇总赋给变量dataall
dataall = "<p>标题为:" + thistitle + "</p><p>内容为:" + thiscontent + "</p><br>"
# 将该篇文章的标题与内容的总信息写入对应文件
fh.write(dataall.encode("utf-8"))
print("第" + str(i) + "个网页第" + str(j) + "次处理") # 便于调试
except urllib.error.URLError as e:
if hasattr(e, "code"):
print(e.code)
if hasattr(e, "reason"):
print(e.reason)
# 若为URLError异常,延时10秒执行
time.sleep(10)
except Exception as e:
print("exception:" + str(e))
# 若为Exception异常,延时1秒执行
time.sleep(1)
fh.close()
# 设置并写入本地文件的html后面结束部分代码
html2 = '''</body>
</html>
'''
fh = open("D:/9.html", "ab")
fh.write(html2.encode("utf-8"))
fh.close()
# 设置关键词
key = "物联网"
# 设置代理服务器,该代理服务器有可能失效,读者需要换成新的有效代理服务器
proxy = "119.6.136.122:80"
# 可以为getlisturl()与getcontent()设置不同的代理服务器,此处没有启用该项设置
proxy2 = ""
# 起始页
pagestart = 1
# 抓取到哪页
pageend = 2
listurl = getlisturl(key, pagestart, pageend, proxy)
getcontent(listurl, proxy)
多线程爬虫实战
多线程基础
import threading
class A(threading.Thread):
def __init__(self):
# 初始化该线程
threading.Thread.__init__(self)
def run(self):
# 该线程要执行的程序内容
for i in range(10):
print("我是线程A")
class B(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
for i in range(10):
print("我是线程B")
# 实例化线程A为t1
t1 = A()
# 启动线程t1
t1.start()
# 实例化线程B为t2
t2 = B()
# 启动线程t2,此时与t1同时执行
t2.start()
'''
output:
我是线程A
我是线程A
我是线程A
我是线程A
我是线程A
我是线程A
我是线程A
我是线程B
我是线程B
我是线程B
我是线程B
我是线程B
我是线程B
我是线程B
我是线程B
我是线程B
我是线程B
我是线程A
我是线程A
我是线程A
Process finished with exit code 0
'''
队列基础
import queue
q = queue.Queue()
q.put('A')
q.task_done()
q.put('B')
q.task_done()
q.put('C')
q.task_done()
print(q.get()) # A
print(q.get()) # B
print(q.get()) # C
实例代码,将上面的单线程微信爬虫改为多线程
import threading
import queue
import re
import urllib.request
import time
import urllib.error
urlqueue = queue.Queue()
# 模拟成浏览器
headers = ("User-Agent",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0")
opener = urllib.request.build_opener()
opener.addheaders = [headers]
# 将opener安装为全局
urllib.request.install_opener(opener)
listurl = []
# 使用代理服务器的函数
def use_proxy(proxy_addr, url):
try:
proxy = urllib.request.ProxyHandler({'http': proxy_addr})
opener = urllib.request.build_opener(proxy, urllib.request.HTTPHandler)
urllib.request.install_opener(opener)
data = urllib.request.urlopen(url).read().decode('utf-8')
return data
except urllib.error.URLError as e:
if hasattr(e, "code"):
print(e.code)
if hasattr(e, "reason"):
print(e.reason)
time.sleep(10)
except Exception as e:
print("exception:" + str(e))
time.sleep(1)
# 线程1,专门获取对应网址并处理为真实网址
class geturl(threading.Thread):
def __init__(self, key, pagestart, pageend, proxy, urlqueue):
threading.Thread.__init__(self)
self.pagestart = pagestart
self.pageend = pageend
self.proxy = proxy
self.urlqueue = urlqueue
self.key = key
def run(self):
page = self.pagestart
# 编码关键词key
keycode = urllib.request.quote(key)
# 编码"&page"
pagecode = urllib.request.quote("&page")
for page in range(self.pagestart, self.pageend + 1):
url = "http://weixin.sogou.com/weixin?type=2&query=" + keycode + pagecode + str(page)
# 用代理服务器爬,解决IP被封杀问题
data1 = use_proxy(self.proxy, url)
# 列表页url正则
listurlpat = '<div class="txt-box">.*?(http://.*?)"'
listurl.append(re.compile(listurlpat, re.S).findall(data1))
# 便于调试
print("获取到" + str(len(listurl)) + "页")
for i in range(0, len(listurl)):
# 等一等线程2,合理分配资源
time.sleep(7)
for j in range(0, len(listurl[i])):
try:
url = listurl[i][j]
# 处理成真实url,读者亦可以观察对应网址的关系自行分析,采集网址比真实网址多了一串amp
url = url.replace("amp;", "")
print("第" + str(i) + "i" + str(j) + "j次入队")
self.urlqueue.put(url)
self.urlqueue.task_done()
except urllib.error.URLError as e:
if hasattr(e, "code"):
print(e.code)
if hasattr(e, "reason"):
print(e.reason)
time.sleep(10)
except Exception as e:
print("exception:" + str(e))
time.sleep(1)
# 线程2,与线程1并行执行,从线程1提供的文章网址中依次爬取对应文章信息并处理
class getcontent(threading.Thread):
def __init__(self, urlqueue, proxy):
threading.Thread.__init__(self)
self.urlqueue = urlqueue
self.proxy = proxy
def run(self):
html1 = '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>微信文章页面</title>
</head>
<body>'''
fh = open("D:/Python35/myweb/part6/2.html", "wb")
fh.write(html1.encode("utf-8"))
fh.close()
fh = open("D:/Python35/myweb/part6/2.html", "ab")
i = 1
while (True):
try:
url = self.urlqueue.get()
data = use_proxy(self.proxy, url)
titlepat = "<title>(.*?)</title>"
contentpat = 'id="js_content">(.*?)id="js_sg_bar"'
title = re.compile(titlepat).findall(data)
content = re.compile(contentpat, re.S).findall(data)
thistitle = "此次没有获取到"
thiscontent = "此次没有获取到"
if (title != []):
thistitle = title[0]
if (content != []):
thiscontent = content[0]
dataall = "<p>标题为:" + thistitle + "</p><p>内容为:" + thiscontent + "</p><br>"
fh.write(dataall.encode("utf-8"))
print("第" + str(i) + "个网页处理") # 便于调试
i += 1
except urllib.error.URLError as e:
if hasattr(e, "code"):
print(e.code)
if hasattr(e, "reason"):
print(e.reason)
time.sleep(10)
except Exception as e:
print("exception:" + str(e))
time.sleep(1)
fh.close()
html2 = '''</body>
</html>
'''
fh = open("D:/Python35/myweb/part6/2.html", "ab")
fh.write(html2.encode("utf-8"))
fh.close()
# 并行控制程序,若60秒未响应,并且存url的队列已空,则判断为执行成功
class conrl(threading.Thread):
def __init__(self, urlqueue):
threading.Thread.__init__(self)
self.urlqueue = urlqueue
def run(self):
while (True):
print("程序执行中")
time.sleep(60)
if (self.urlqueue.empty()):
print("程序执行完毕!")
exit()
key = "人工智能"
proxy = "119.6.136.122:80"
proxy2 = ""
pagestart = 1 # 起始页
pageend = 2 # 抓取到哪页
# 创建线程1对象,随后启动线程1
t1 = geturl(key, pagestart, pageend, proxy, urlqueue)
t1.start()
# 创建线程2对象,随后启动线程2
t2 = getcontent(urlqueue, proxy)
t2.start()
# 创建线程3对象,随后启动线程3
t3 = conrl(urlqueue)
t3.start()
爬虫浏览器的伪装技术
常见的反爬虫机制:
- 通过分析用户请求的Headers信息进行反爬虫。
- 通过检测用户行为进行反爬虫,比如判断同一个IP在短时间内是否频繁访问对应网站等进行分析。
- 通过动态页面增加爬虫爬取的难度,达到反爬虫的目的。
解决思路:
第一种,伪装成浏览器,设置好Headers字段,如User-Agent ,Referer
第二种,代理服务器,
第三种,利用一些工具软件,比如selenium+phanotomJS
访问优酷官网,用Fiddler查看头信息
GET / HTTP/1.1
Host: www.youku.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://home.firefoxchina.cn/
Cookie: __ysuid=1515480667604MVu; premium_cps=3746062939_40%7C73%7C658%7C238___
Connection: keep-alive
Upgrade-Insecure-Requests: 1
各字段含义如下:
GET / HTTP/1.1 请求方式与请求协议
Host 请求的服务器地址
其他常见字段:
常见字段1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept 字段主要用来表示浏览器能够支持的内容类型有哪些
text/html 表示HTML文档
application/xhtml+xml 表示XHTML文档
application/xml 表示XML文档
q 代表权重系数,值介于0和1之间
所有这一行字段信息表示:浏览器可以支持text/html,application/xhtml+xml,application/xml,*/*内容类型,支持的优先顺序从左到右依次排列。
常见字段2
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Language 主要表示浏览器所支持的语言类型
zh-CN 表示简体中文,zh代表中文,CN代表简体
en-US 代表英语(美国)语言
en 代表英语语言
所以这一行字段信息表示,浏览器支持zh-CN,zh,en-US,en等语言
常见字段3
Accept-Encoding: gzip, deflate
Accept-Language 字段主要用来表示浏览器支持的压缩编码格式有哪些
gzip 是压缩编码的一种
deflate 是一种无损数据压缩算法
这一行字段信息表示浏览器可以支持gzip,deflate压缩编码
常见字段4
Referer: http://home.firefoxchina.cn/
Referer 字段表示来源网站的地址,就是说从那个网站过来的
常见字段5
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
User-Agent 表示用户代理
Mozilla/5.0 表示浏览器及版本信息
Windows NT 10.0; WOW64; rv:52.0表示客户端操作系统对应信息
Gecko/20100101 表示网页版引擎对应信息
Firefox/52.0 表示火狐浏览器
常见字段6
Connection: keep-alive
Connection 表示客户端与服务端链接类型,对应的字段值主要有两种:
keep-alive 表示持久性链接
close 表示单方面关闭链接,让链接断开
所以此时,这一字段信息表示客户端与服务端的链接是持久性链接
《精通python网络爬虫》笔记的更多相关文章
- HTML+CSS笔记 CSS笔记集合
HTML+CSS笔记 表格,超链接,图片,表单 涉及内容:表格,超链接,图片,表单 HTML+CSS笔记 CSS入门 涉及内容:简介,优势,语法说明,代码注释,CSS样式位置,不同样式优先级,选择器, ...
- CSS笔记--选择器
CSS笔记--选择器 mate的使用 <meta charset="UTF-8"> <title>Document</title> <me ...
- HTML+CSS笔记 CSS中级 一些小技巧
水平居中 行内元素的水平居中 </a></li> <li><a href="#">2</a></li> &l ...
- HTML+CSS笔记 CSS中级 颜色&长度值
颜色值 在网页中的颜色设置是非常重要,有字体颜色(color).背景颜色(background-color).边框颜色(border)等,设置颜色的方法也有很多种: 1.英文命令颜色 语法: p{co ...
- HTML+CSS笔记 CSS中级 缩写入门
盒子模型代码简写 回忆盒模型时外边距(margin).内边距(padding)和边框(border)设置上下左右四个方向的边距是按照顺时针方向设置的:上右下左. 语法: margin:10px 15p ...
- HTML+CSS笔记 CSS进阶再续
CSS的布局模型 清楚了CSS 盒模型的基本概念. 盒模型类型, 我们就可以深入探讨网页布局的基本模型了.布局模型与盒模型一样都是 CSS 最基本. 最核心的概念. 但布局模型是建立在盒模型基础之上, ...
- HTML+CSS笔记 CSS进阶续集
元素分类 在CSS中,html中的标签元素大体被分为三种不同的类型:块状元素.内联元素(又叫行内元素)和内联块状元素. 常用的块状元素有: <div>.<p>.<h1&g ...
- HTML+CSS笔记 CSS进阶
文字排版 字体 我们可以使用css样式为网页中的文字设置字体.字号.颜色等样式属性. 语法: body{font-family:"宋体";} 这里注意不要设置不常用的字体,因为如果 ...
- HTML+CSS笔记 CSS入门续集
继承 CSS的某些样式是具有继承性的,那么什么是继承呢?继承是一种规则,它允许样式不仅应用于某个特定html标签元素,而且应用于其后代(标签). 语法: p{color:red;} <p> ...
- HTML+CSS笔记 CSS入门
简介: </span>年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的<span>脚本解释程序</span>,作为ABC语言的一种继承. & ...
随机推荐
- CodeForces - 363D --二分和贪心
题目:CodeForces - 363D 题意:给定n个学生,其中每个学生都有各自的私己钱,并且自己的私己钱只能用在自己买自行车,不能给别人. 给定m个自行车,每个自行车都有一个价格. 给定公有财产a ...
- MongoDB 教程(八):查询文档、条件操作符
MongoDB 查询文档 MongoDB 查询文档使用 find() 方法. find() 方法以非结构化的方式来显示所有文档. MongoDB 查询数据的语法格式如下: db.collection. ...
- html5(一)
HTML5 三个基本特色:结构.样式.功能. <!DOCTYPE html ><html lang="en"><head> <meta c ...
- 1.3 第一个Go程序
1.3.1 Hello Go // hello.go package main import ( "fmt" ) func main() { fmt.Println("H ...
- VIM入门
目录 一.vim介绍 二.vim颜色显示和移动光标 三.vim一般模式下移动光标 四.vim一般模式下复制.剪切和粘贴 五.进入编辑模式 六.vim命令模式 七.vim实践 一.vim介绍 vi是最重 ...
- kafka 分区和副本以及kafaka 执行流程,以及消息的高可用
1.Kafka概览 Apache下的项目Kafka(卡夫卡)是一个分布式流处理平台,它的流行是因为卡夫卡系统的设计和操作简单,能充分利用磁盘的顺序读写特性.kafka每秒钟能有百万条消息的吞吐量,因此 ...
- 一分钟学会ConstraintLayout(转载)
原文地址:https://www.v2ex.com/t/287863 最近更新了Android Studio,突然发现xml中的布局已经变成了ConstraintLayout,于是搜了一篇文章看一下 ...
- 剑指Offer 11. 二进制中1的个数 (其他)
题目描述 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 题目地址 https://www.nowcoder.com/practice/8ee967e43c2c4ec193b040e ...
- 3070 Fibonacci
Fibonacci Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 21048 Accepted: 14416 Descr ...
- 使用maven-shade-plugin打包spring项目为可执行的jar包
使用maven-shade-plugin打包spring项目为可执行的jar包,打包后的jar包里面包含依赖的jar包. POM文件: <plugin> <groupId>or ...