五年Python爬虫程序员整理的全栈爬虫知识点
1 最简单的单页面抓取
思路:
- 获取页面所有url
- 对获取的所有url进行分类
- A 获取属于本域名下的url
- B 获取属于其他url
2 用到的模块
- urllib
- bs4
- re正则表达式
有需要Python学习资料的小伙伴吗?小编整理一套Python资料和PDF,感兴趣者可以关注小编后私信学习资料(是关注后私信哦)反正闲着也是闲着呢,不如学点东西啦
3 代码说明:
import urllib from bs4 import BeautifulSoup import re def get_all_url(url): urls = [] web = urllib.urlopen(url)#使用urllib模块的urlopen函数打开url,复制给web soup =BeautifulSoup(web.read())#将web内容转化为beautigulsoup格式的数据。 #通过正则过滤合理的url(针对与freebuf.com来讲) tags_a =soup.findAll(name='a',attrs={'href':re.compile("^https?://")}) #soup.findall函数的运用,配合正则表达式来过滤url try : for tag_a in tags_a: #re:^ 表示匹配字符串开头例如,^ abc 匹配 abc ? 表示匹配前一个字符串0次或1次,例如 abc? 匹配 ab 和 abc #return urls except: pass return urls #得到所有freebuf.com下的url def get_local_urls(url): local_urls = [] urls = get_all_url(url) for _url in urls: ret = _url if 'freebuf.com' in ret.replace('//','').split('/')[0]: local_urls.append(_url) return local_urls #if 'freebuf.com' in ret.replace('//','').split('/')[0]:这个if语句不是很明白,通过split()函数,把域名分割,获取分割后组成的类表的第一个字符串。但是在分割前为什么要把//替换成空格??? #得到所有的不是freebuf.com域名的url def get_remote_urls(url): remote_urls = [] urls = get_all_url(url) for _url in urls: ret = _url if "freebuf.com" not in ret.replace('//','').split('/')[0]: remote_urls.append(_url) return remote_urls #主函数 def __main__(): url = 'http://freebuf.com/' rurls = get_remote_urls(url) print "--------------------remote urls-----------------------" for ret in rurls: print ret print "---------------------localurls-----------------------" lurls = get_local_urls(url) for ret in lurls: print ret if __name__ == '__main__': __main__()
上面是单独对一个页面抓取。如果对整个站点抓取的话,还设计到url的处理,用作者的原话:
我们可以把整站当成一个错综复杂的图结构,有一些算法基础的读者都会知道图的简单遍历方法:dfs和bfs(深度优先和广度优先)。如果这里读者有问题的话建议先去学习一下这两种算法。大体的算法结构我们清楚了,但是在实现中我们显然需要特殊处理url,需要可以区分当前目标站点域名下的网站和其他域名的网站,除此之外,在href的值中经常会出现相对url,这里也要特别处理。
下面是代码:
import urllib from bs4 import BeautifulSoup import urlparse import time import urllib2 #urllib 和 urllib2的区别http://blog.csdn.net/dolphin_h/article/details/45296353 url = "http://xxxx.xx/" domain = "xxxx.xx" deep = 0 tmp = "" sites = set() visited = set() #local = set() #集合: python的set和其他语言类似, 是一个无序不重复元素集, 基本功能包括关系测试和消除重复元素. 集合对象还支持union(联合), intersection(交), difference(差)和sysmmetric difference(对称差集)等数学运算. def get_local_pages(url,domain): global deep global sites global tmp repeat_time = 0 pages = set() #防止url读取卡住 while True: try: print "Ready to Open the web!" time.sleep(1) #time.sleep()函数, Python time sleep() 函数推迟调用线程的运行,可通过参数secs指秒数,表示进程挂起的时间 print "Opening the web", url web = urllib2.urlopen(url=url,timeout=3) print "Success to Open the web" break except: print "Open Url Failed !!! Repeat" time.sleep(1) repeat_time = repeat_time+1 if repeat_time == 5: return #上面整段判断url能不能打开 print "Readint the web ..." soup = BeautifulSoup(web.read()) print "..." #提取标签 a for tag in tags: #避免参数传递异常 try: ret = tag['href'] except: print "Maybe not the attr : href" continue o = urlparse.urlparse(ret) #urlparse.urlparse函数的使用 urlparse模块主要是把url拆分为6部分,并返回元组。并且可以把拆分后的部分再组成一个url。主要有函数有urljoin、urlsplit、urlunsplit、urlparse等。 urlparse.urlparse(urlstring[, scheme[, allow_fragments]]) 将urlstring解析成6个部分,它从urlstring中取得URL,并返回元组 (scheme, netloc, path, parameters, query, fragment),但是实际上是基于namedtuple,是tuple的子类。它支持通过名字属性或者索引访问的部分URL,每个组件是一串字符,也有可能是空的。组件不能被解析为更小的部分,%后面的也不会被解析,分割符号并不是解析结果的一部分,除非用斜线转义,注意,返回的这个元组非常有用,例如可以用来确定网络协议(HTTP、FTP等等 )、服务器地址、文件路径,等等。 """ #Debug I/O for _ret in o: if _ret == "": pass else: print _ret """ #处理相对路径url if o[0] is "" and o[1] is "": print "Fix Page: " +ret url_obj = urlparse.urlparse(web.geturl()) #获取web页面的url,用urlparse函数抽离 ret = url_obj[0] + "://" + url_obj[1] + url_obj[2] + ret #组成一个绝对url #保持url的干净 ret = ret[:8] + ret[8:].replace('//','/') o = urlparse.urlparse(ret) #这里不是太完善,但是可以应付一般情况 if '../' in o[2]: paths = o[2].split('/') for i inrange(len(paths)): if paths[i] == '..': paths[i] = '' if paths[i-1]: paths[i-1] = '' tmp_path = '' for path in paths: if path == '': continue tmp_path = tmp_path + '/' +path ret =ret.replace(o[2],ret_path) print "FixedPage: " + ret 上面整段都是判断获到的url是绝对url还是相对url。如果是相对url,则还需进行重组成完善的绝对url。包括url中含../的情况进行处理。但是处理../的情况不理解!!??? #协议处理 if 'http' not in o[0]: print "Bad Page:" + ret.encode('ascii') continue #url合理性检验 if o[0] is "" and o[1] is not "": print "Bad Page: " +ret continue #域名检验 if domain not in o[1]: #变量domain用来检验获取的所有url是否是改域名下的 print "Bad Page: " +ret continue #整理,输出 newpage = ret if newpage not in sites: print "Add New Page: " + newpage pages.add(newpage) return pages #dfs算法遍历全站 def dfs(pages): #无法获取新的url说明便利完成,即可结束dfs if pages is set(): return global url global domain global sites global visited sites = set.union(sites,pages) for page in pages: if page not in visited: print "Visiting",page visited.add(page) url = page pages = get_local_pages(url, domain) dfs(pages) print "sucess" #整段程序下来,一直不知道 变量domain用来检验获取的所有url是否是改域名下的 pages = get_local_pages(url, domain) dfs(pages) for i in sites: print i
整个的大概思路是:
- 传入url
- 判断能否打开
- 打开url,获取整个改url的web信息
- 提取属性‘href’
- 遍历提取到的链接,这里叫做url2
- 判断url是否为相对url
- 是的话
- 对其进行抽离[urlparse.urlparse()]
- 重组
- (处理相对url)
- 最后检验协议、url域名。
- 带入遍历算法。
- 最后是web元素的处理
两个模块
bs4模块和docx模块的使用
**bs4前面有写
我们重点要讲findAll方法的两个参数:name和attr**
Name: 指的是标签名,传入一个标签名的名称就可以返回所有固定名称的标签名
Attr: 是一个字典存储需要查找的标签参数,返回对应的标签
- Tag.children 表示获取tag标签的所有子标签
- Tag.string 表示获取tag标签内的所有字符串,不用一层一层索引下去寻找字符串
- Tag.attrs[key] 表示获取tag标签内参数的键值对键为key的值
- Tag.img 表示获取tag标签的标签名为img的自标签(一个)
docx模块:
在使用这个模块的时候,要记清楚如果:
pip install python-docx
easy_install python-docx
两种方式安装都不能正常使用的话,就需要下载tar包自己手动安装
Docx模块是一个可以直接操作生成docx文档的python模块,使用方法极尽简单:
- Demo = Document() #在内存中建立一个doc文档
- Demo.add_paragraph(data) #在doc文档中添加一个段落
- Demo.add_picture(“pic.png”) #doc文档中添加一个图片
- Demo.save(‘demo.docx’) #存储docx文档
观察html结构:
我们大致观察一下结构,定位到文章的具体内容需要找到标签,然后再遍历标签的子标签即可遍历到所有的段落,配图资料
这样定位到图片,那么我们怎么样来寻找
from docx import Document from bs4 import BeautifulSoup import urllib url ="http://freebuf.com/news/94263.html" data = urllib.urlopen(url) document = Document() soup = BeautifulSoup(data) article = soup.find(name ="div",attrs={'id':'contenttxt'}).children #这段是提取我们所要的信息 for e in article: try: if e.img: pic_name = '' print e.img.attrs['src'] if 'gif' in e.img.attrs['src']: pic_name = 'temp.gif' elif 'png' in e.img.attrs['src']: pic_name = 'temp.png' elif 'jpg' in e.img.attrs['src']: pic_name = 'temp.jpg' else: pic_name = 'temp.jpeg' urllib.urlretrieve(e.img.attrs['src'], filename=pic_name) #下面我们再来看看 urllib 模块提供的 urlretrieve() 函数。urlretrieve() 方法直接将远程数据下载到本地。 #1 >>> help(urllib.urlretrieve) #2 Help on function urlretrieve in module urllib: #3 #4 urlretrieve(url, filename=None, reporthook=None, data=None) #参数 finename 指定了保存本地路径(如果参数未指定,urllib会生成一个临时文件保存数据。) #参数 reporthook 是一个回调函数,当连接上服务器、以及相应的数据块传输完毕时会触发该回调,我们可以利用这个回调函数来显示当前的下载进度。 #参数 data 指 post 到服务器的数据,该方法返回一个包含两个元素的(filename, headers)元组,filename 表示保存到本地的路径,header 表示服务器的响应头。 document.add_picture(pic_name) except: pass if e.string: print e.string.encode('gbk','ignore') document.add_paragraph(e.string) document.save("freebuf_article.docx") print "success create a document"
主要是作为笔记,记录个人学习过程。
大部分内容转载,如若侵权,请联系删除。
有需要Python学习资料的小伙伴吗?小编整理一套Python资料和PDF,感兴趣者可以关注小编后私信学习资料(是关注后私信哦)反正闲着也是闲着呢,不如学点东西啦