作者:黑痣佬 | 来源:互联网 | 2023-09-07 12:22
一、文章说明
本文是在学习过程中的笔记分享,开发环境是win7,Python3,编辑器pycharm,文章中若有错误欢迎指出、积极讨论。
另外,推荐一个比较好的爬虫教程
二、课程基础
1、HTML和CSS
爬虫和网页内容处处打交道,首先要掌握一部分前端内容。参考教程:W3school在线教程
2、xpath解析网页
掌握了上面的知识,下面就可以开始下一步学习了。如何解析网页?这里我推荐BeautifulSoup和xpath,掌握了这两种解析方法基本上就够了,当然,还有一种必须掌握:正则表达式,有点简单粗暴,但屡试不爽
3、http响应状态
2xx:成功
3xx:调转
4xx:客户端错误
5xx:服务器错误
三、爬取过程的选择策略
一般我们爬取都有一个明确的目标,如知道要爬那些网页、网页上的那些内容、需要爬多少等。但是当我们要对一个网站进行无脑爬取时,应综合考虑如下策略:
1、重要的网页距离种子站点比较近
2、深度有限,一般17层,再往深处爬无意义
3、宽度优先有利于多爬虫并行爬取
4、深度限制与宽度优先相结合
四、如何记录爬取历史,不重复抓取?
1、将URL经过MD5或SHA-1等单向哈希后再保存到hashset或数据库,这样每一个URL保存下来就只占16个字节。
2、Bit-Map方法。建立一个BitSet,将每个URL经过一个哈希函数映射到某一位,只占1字节。
技巧:看一个站点有多少信息,以便于我们估计内存消耗
百度:site:www.mafengwo.cn
我们可以看到蚂蜂窝有多少个网页。
同样,Google: site:www.mafengwo.cn ,更厉害的是Google能看到种子站点下一个站点的网页信息:
site:www.mafengwo.cn/gonglve/
3、BitMap方式记录
pip install bitarray
pip install mmh3
>>> from bitarray import bitarray
>>> import mmh3
>>> a = 2**31
>>> a
2147483648
>>> offset = 2147483647
>>> offset = 2147483647//2**31-1
>>> bit_array = bitarray(4*1024*1024*1023)
>>> #分配4G内存
>>> bit_array.setall(0) #内存位置初始化为0
>>> b1 = mmh3.hash('www.baidu.com',42)+offset #42是固定设置,offset将偏置设为0,索引从0开始,b1返回int类型
>>> bit_array[b1] = 1 #值为0或1,如果该位置没有占用,就按照默认0,如果占用就是1
4、Bloom Filter 算法
参考教程:Bloom Filter 算法
pip install pybloom
>>> import pybloom
>>> fruit = pybloom.BloomFilter(100000,0.1) #0.1 容错率
>>> fruit.update('apple')
Traceback (most recent call last):
File "", line 1, in
fruit.update('apple')
AttributeError: 'BloomFilter' object has no attribute 'update'
>>> fruit.add('apple')
False
>>> len(fruit) #fruit包含的元素个数
1
>>> fruit.add('pear','orange','apple')
Traceback (most recent call last):
File "", line 1, in
fruit.add('pear','orange','apple')
TypeError: add() takes from 2 to 3 positional arguments but 4 were given
>>> fruit.union('pear','orange','apple')
Traceback (most recent call last):
File "", line 1, in
fruit.union('pear','orange','apple')
TypeError: union() takes 2 positional arguments but 4 were given
>>> fruit.add('pear') #只能添加一个??如果fruit内不包含返回False,反之,True
False
>>> fruit.add('orange')
False
>>> fruit.add('apple')
True
>>> len(fruit)
3
>>> 'mike' in fruit
False
>>> 'apple' in fruit
True
>>>
技巧:在某些网站robots.txt页面下有该网站的所有网页信息 www.xxxxxxxxx.xml
sitemap:
五、实战案例
仅做测试:
获取蚂蜂窝城市游记
代码:
环境:win7,Python3,pycharm
import urllib.request
import http.client
import re
from pybloom import BloomFilter
import os
request_headers = {
'host': "www.mafengwo.cn",
'connection': "keep-alive",
'cache-control': "no-cache",
'upgrade-insecure-requests': "1",
'user-agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36",
'accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
'accept-language': "zh-CN,en-US;q=0.8,en;q=0.6"
}
def get_html(url):
req = urllib.request.Request(url, headers=request_headers)
respOnse= urllib.request.urlopen(req)
html = response.read()
return html
def download_city_notes(id):
for i in range(1, 999):
url = 'http://www.mafengwo.cn/yj/%s/1-0-%d.html' % (id, i)
if url in download_bf:
continue
print ('open url %s' %url)
download_bf.add(url)
html = get_html(url)
htmlcOntent= html.decode('utf-8')
city_notes = re.findall('href="/i/d{7}.html', htmlcontent)
# 如果导航页错误,该页的游记数为0,则意味着 1-0-xxx.html 已经遍历完,结束这个城市
if len(city_notes) == 0:
return
for city_note in city_notes:
try:
city_url = 'http://www.mafengwo.cn%s' % (city_note[6:])
if city_url in download_bf:
continue
print ('download %s' % (city_url))
html = get_html(city_url)
filename = city_url[7:].replace('/', '_')
fo = open("%s%s" % (dirname, filename), 'wb+')
fo.write(html)
fo.close()
download_bf.add(city_url)
except Exception as Arguments:
print (Arguments)
continue
#global
city_home_pages = []
city_ids = []
dirname = 'mafengwo_notes/'
# 创建 Bloom Filter
download_bf = BloomFilter(1024 * 1024 * 16, 0.01)
def main():
# 检查用于存储网页文件夹是否存在,不存在则创建
if not os.path.exists(dirname):
os.makedirs(dirname)
try:
# 下载目的地的首页
mdd_url = 'http://www.mafengwo.cn/mdd/'
html = get_html(mdd_url)
htmlcOntent= html.decode('utf-8') #正则表达式匹配时需要解码
# 利用正则表达式,找出所有的城市主页
city_home_pages = re.findall('/travel-scenic-spot/mafengwo/d{5}.html', htmlcontent)
# 通过循环,依次下载每个城市下的所有游记
for city in city_home_pages:
city_ids.append(city[29:34])
download_city_notes(city[29:34])
except urllib.request.HTTPError as Arguments:
print (Arguments)
except http.client.BadStatusLine:
print ('BadStatusLine')
except Exception as Arguments:
print (Arguments)
if __name__ == '__main__':
main()
好的!!暂时就这么多了
第一次将笔记写在CSDN上,太难写了,主要太浪费时间还要写得好看
希望坚持,将整个课程写完