[Python]新手写爬虫全过程(已完成)
今天早上起来,第一件事情就是理一理今天该做的事情,瞬间get到任务,写一个只用python字符串内建函数的爬虫,定义为v1.0,开发中的版本号定义为v0.x。数据存放?这个是一个练手的玩具,就写在txt文本里吧。其实主要的不是学习爬虫,而是依照这个需求锻炼下自己的编程能力,最重要的是要有一个清晰的思路(我在以这个目标努力着)。ok,主旨已经订好了,开始‘撸串’了。
目标网站:http://bohaishibei.com/post/category/main/(一个很有趣的网站,一段话配一个图,老有意思了~)网站形式如下:
目标:把大的目标分为几个小的目标。因为第一次干这个,所以对自己能力很清楚,所以完成顺序由简单到复杂。
1.爬取一期的内容,包括标题,和图片的url
2.把数据存在本地的txt文件中
3.想爬多少就爬就爬少
4.写一个网站,展示一下。(纯用于学习)
Let‘s 搞定它!
时间——9:14
把昨天晚上做的事情交代一下。昨天晚上写的代码实现了爬取一期里的所有标题。
第一步:
我用的是google浏览器,进入开发者模式,使用’页面内的元素选择器‘,先看一下内页中的结构,找到我们要的数据所在’标签‘。
这里我们需要的博海拾贝一期的内容全部在<article class="article-content">这个标签里面,如下图:
第一条红线是:页面内的元素选择器
第二条是:内容所在标签
第三条是:title
经过分析得出,我只要<article class="article-content">,这个标签的内容:所以写了下面的方法:
- def content(html):
- # 内容分割的标签
- str = '<article class="article-content">'
- content = html.partition(str)[2]
- str1 = '<div class="article-social">'
- content = content.partition(str1)[0]
- return content # 得到网页的内容
这里需要说一下:在写这个爬虫之前我就打算只用字符串的内置函数来处理匹配问题,所以我就上http://www.w3cschool.cc/python/进入到字符串页面,大致看了一遍字符串的内建函数有哪些。
partition() 方法用来根据指定的分隔符将字符串进行分割。
如果字符串包含指定的分隔符,则返回一个3元的元组,第一个为分隔符左边的子串,第二个为分隔符本身,第三个为分隔符右边的子串。
partition() 方法是在2.5版中新增的。参考:http://www.w3cschool.cc/python/att-string-partition.html
这样我就得到只有内容的字符串了,干净~
第二步:
得到title的内容。title的格式如下,我只要’【2】‘后面的文字,后面的img暂时不考虑一步步的来。
<p>【2】这是我最近的状态,请告诉我不是我一个人!</p><p><img src=http://ww4.sinaimg.cn/mw690/005CfBldtw1etay8ifthnj30an0aot8w.jpg /></p><p>
我写了下面的方法:
- def title(content,beg = 0):
- # 思路是利用str.index()和序列的切片
- try:
- title_list = []
- while True:
- num1 = content.index('】',beg)
- num2 = content.index('</p>',num1)
- title_list.append(content[num1:num2])
- beg = num2
- except ValueError:
- return title_list
这里用try....except是因为我不知道怎么跳出循环。。。。求大神有更好的方法告诉我。
我这里跳出循环用的是当抛出VlaueError异常就说明找不到了,那就返回列表。就跳出循环了。
num1是】的位置,num2是</p>的位置,然后用序列的切片,咔嚓咔嚓一下就是我想要的数据了。这里需要注意的是:切片’要头不要尾‘所以我们的得到的数据就是这个样子的:
哎呀,这个是什么鬼!要头不要尾就是这个意思!
然后我就想:那就把num1加1不就完了吗?我真是太天真了。。。。
请+3,我觉得原理是这样的,这个是个中文字符!(求大神指点)
第三步:
交代清楚我昨天晚上做的事情了,记录下时间——10:01,下面我要爬图片的url了。这里要说一下,如果要把图片下下来,最重要的一步就是得到url,然后下载下来保存到本地(用文本的IO)。
我先获得url,实现原理同获取title,我在想,既然一样卸载获取title的方法里好,还是在写一个方法好。我单独写了一个方法,但是其实就是复制了一下title的方法,改了下匹配的字符串,代码如下:
- def img(content,beg = 0):
- # 思路是利用str.index()和序列的切片
- try:
- img_list = []
- while True:
- src1 = content.index('http',beg)
- src2 = content.index('/></p>',src1)
- img_list.append(content[src1:src2])
- beg = src2
- except ValueError:
- return img_list
结果图如下:
这里发现,有的时候一个title会有很多个图片。我思考之后有如下思路:
1.需要写一个方法,当一个title出现多个图片的时候,捕获url。这个需要有一个判断语句,当url长度大于一个url长度的时候,才需要调用这个函数。
2.多个图片的url怎么放?使用符号隔开存放还是嵌套放入一个数组里面?我这里打算用’|‘隔开,这样的话加一个判语句,或者先判断一下url长度,都可以进行。
这个问题先放在这里,因为当我要下载的时候这个url才需要过滤,所以先进行下一步,把数据存到本地txt文中,这里在解决这个问题也不晚。
第四步:
把数据存到本地的txt中。Python文件IO参考资料:http://www.w3cschool.cc/python/python-files-io.html
这里需要注意的是,文本写入的时候记得close,还有就是注意打开文本的模式。
时间——11:05 吃个饭先
时间——11:44 回来了
这里我考虑了一个问题,根据《编写高质量代码——改善python程序的91个建议》这本书中写道的,字符串连接时,用jion()效率高于’+‘
所以我写了如下代码:
- def data_out(data):
- #这里写成一个方法好处是,在写入文本的时候就在这里写
- fo = open("/home/qq/data.txt", "a+") #这里注意重新写一个地址
- #for i,e in enumerate(data):
- fo.write("\n".join(data));
- #print '第%d个,title:%s' % (i,e)
- # 关闭打开的文件
- fo.close()
这样造成了一个问题,看图
造成最后一个和新的一个列表写入时在同一行。同时用with....as更好。修改后代码如下:
- def data_out(data):
- #写入文本
- with open("/home/qq/foo.txt", "a+") as fo:
- fo.write('\n')
- fo.write("\n".join(data));
下面研究title和img以什么样的格式存入txt文本:
title$img
这里我有一个概念混淆了,+和join()方法的效率问题主要在连接多个字符串的时候,我这个只用连接一次,不需要考虑这个问题。
- def data_out(title, img):
- #写入文本
- with open("/home/qq/foo.txt", "a+") as fo:
- fo.write('\n')
- size =
- for size in range(0, len(title)):
- fo.write(title[size]+'$'+img[size]+'\n');
文本中的内容如下:
- 愿你贪吃不胖,愿你懒惰不丑,愿你深情不被辜负。$http://ww1.sinaimg.cn/mw690/005CfBldtw1etay8dl1bsj30c50cbq4m.jpg"
- 这是我最近的状态,请告诉我不是我一个人!$http://ww4.sinaimg.cn/mw690/005CfBldtw1etay8ifthnj30an0aot8w.jpg
- 引诱别人和你击拳庆祝,然后偷偷把手势变成二,就可以合体成为蜗牛cosplay……$http://ww2.sinaimg.cn/mw690/005CfBldtw1etay8fzm1sg30b40644qq.gif
- 原来蜗牛是酱紫吃东西的。。。。涨姿势!$http://ww4.sinaimg.cn/mw690/005CfBldtw1etay8egg8vg30bo08ax6p.gif
写入文本的最后,解决多个图片的问题:
- def many_img(data,beg = 0):
- #用于匹配多图中的url
- try:
- many_img_str = ''
- while True:
- src1 = data.index('http',beg)
- src2 = data.index(' /><br /> <img src=',src1)
- many_img_str += data[src1:src2]+'|' # 多个图片的url用"|"隔开
- beg = src2
- except ValueError:
- return many_img_str
- def data_out(title, img):
- #写入文本
- with open("/home/qq/data.txt", "a+") as fo:
- fo.write('\n')
- for size in range(0, len(title)):
- # 判断img[size]中存在的是不是一个url
- if len(img[size]) > 70:
- img[size] = many_img(img[size])# 调用many_img()方法
- fo.write(title[size]+'$'+img[size]+'\n')
输出如下:
- 元气少女陈意涵 by @TopFashionStyle$http://ww2.sinaimg.cn/mw690/005CfBldtw1etay848iktj30bz0bcq4x.jpg|http://ww1.sinaimg.cn/mw690/005CfBldtw1etay83kv5pj30c10bkjsr.jpg|http://ww3.sinaimg.cn/mw690/005CfBldtw1etay82qdvsj30c10bkq3z.jpg|http://ww1.sinaimg.cn/mw690/005CfBldtw1etay836z8lj30c00biq40.jpg|http://ww4.sinaimg.cn/mw690/005CfBldtw1etay8279qmj30ac0a0q3p.jpg|http://ww1.sinaimg.cn/mw690/005CfBldtw1etay81ug5kj30c50bnta6.jpg|http://ww2.sinaimg.cn/mw690/005CfBldtw1etay8161ncj30c20bgmyt.jpg|http://ww2.sinaimg.cn/mw690/005CfBldtw1etay804oy7j30bs0bgt9r.jpg|
暂时功能是实现了,后面遇到问题需要修改在改吧。。。。新手走一步看一步!!!
到此为止,已经完成了前两个简单的计划:
1.爬取一期的内容,包括标题,和图片的url
2.把数据存在本地的txt文件中
全部代码如下:
- #coding:utf-8
- import urllib
- ######
- #爬虫v0.1 利用urlib 和 字符串内建函数
- ######
- def getHtml(url):
- # 获取网页内容
- page = urllib.urlopen(url)
- html = page.read()
- return html
- def content(html):
- # 内容分割的标签
- str = '<article class="article-content">'
- content = html.partition(str)[2]
- str1 = '<div class="article-social">'
- content = content.partition(str1)[0]
- return content # 得到网页的内容
- def title(content,beg = 0):
- # 匹配title
- # 思路是利用str.index()和序列的切片
- try:
- title_list = []
- while True:
- num1 = content.index('】',beg)+3
- num2 = content.index('</p>',num1)
- title_list.append(content[num1:num2])
- beg = num2
- except ValueError:
- return title_list
- def get_img(content,beg = 0):
- # 匹配图片的url
- # 思路是利用str.index()和序列的切片
- try:
- img_list = []
- while True:
- src1 = content.index('http',beg)
- src2 = content.index('/></p>',src1)
- img_list.append(content[src1:src2])
- beg = src2
- except ValueError:
- return img_list
- def many_img(data,beg = 0):
- #用于匹配多图中的url
- try:
- many_img_str = ''
- while True:
- src1 = data.index('http',beg)
- src2 = data.index(' /><br /> <img src=',src1)
- many_img_str += data[src1:src2]+'|' # 多个图片的url用"|"隔开
- beg = src2
- except ValueError:
- return many_img_str
- def data_out(title, img):
- #写入文本
- with open("/home/qq/data.txt", "a+") as fo:
- fo.write('\n')
- for size in range(0, len(title)):
- # 判断img[size]中存在的是不是一个url
- if len(img[size]) > 70:
- img[size] = many_img(img[size])# 调用many_img()方法
- fo.write(title[size]+'$'+img[size]+'\n')
- content = content(getHtml("http://bohaishibei.com/post/10475/"))
- title = title(content)
- img = get_img(content)
- data_out(title, img)
- # 实现了爬的单个页面的title和img的url并存入文本
时间——15:14
下面要重新分析网站,我已经可以获得一期的内容了,我现在要得到,其它期的url,这样就想爬多少就爬多少了。
目标网址:http://bohaishibei.com/post/category/main/
按照上面的方法进入开发者模式分析网站结构,找出目标数据所在的标签,撸它!
在首页中需要的数据全部都在<div class="content">标签里,分隔方法如下:
- def main_content(html):
- # 首页内容分割的标签
- str = '<div class="content">'
- content = html.partition(str)[2]
- str1 = '</div>'
- content = content.partition(str1)[0]
- return content # 得到网页的内容
我暂时需要的数据:每一期的名字和每一期的url。
经过我的分析:该网站的每期的url格式是这样的:"http://bohaishibei.com/post/10189/"只有数字是变化的。
后来我又发现,我想要的这两个数据都在<h2>这个标签下面,获取每期url的方法如下:
- def page_url(content, beg = 0):
- try:
- url = []
- while True:
- url1 = content.index('<h2><a href="',beg)+13
- url2 = content.index('" ',url1)
- url.append(content[url1:url2])
- beg = url2
- except ValueError:
- return url
title的格式,
我思考了一下,我要title其实没什么太大的意思,用户有不可能说我要看那期,只需要输入看多少期就可以了,标题没有什么实际意义(不像内容中的title是帮助理解改张图笑点的)。所以我打算在这个版本中只实现,你输入想查看要多少期,就返回多少期!
那么下面就需要一个策略了:
http://bohaishibei.com/post/category/main/ 共20期
http://bohaishibei.com/post/category/main/page/2/ 共20期
......
经查看,每页都是20期
当你要查看的期数,超过20期的时候需要,增加page的数值,进入下一页进行获取
最后一页为这个:http://bohaishibei.com/post/category/main/page/48/
实现代码,这个我要想一想怎么写,我是第一次写爬虫,不要嘲讽我啊!
时间——17:09
感觉快实现了,还在写:
- def get_order(num):
- page = num / 20
- order = num % 20 # 超出一整页的条目
- for i in range(1, page+1): # 需这里需要尾巴
- url = 'http://bohaishibei.com/post/category/main/page/%d' % i
- print url
- if (i == page)&(order > 0):
- url = 'http://bohaishibei.com/post/category/main/page/%d' % (i+1)
- print url+",%d条" % order
get_order(55)
运行结果:
- http://bohaishibei.com/post/category/main/page/1
- http://bohaishibei.com/post/category/main/page/2
- http://bohaishibei.com/post/category/main/page/3,15条
- 2
- ~~~~~~~~~~~~
- 15
这里我考虑是这样的我需要重写 page_url,需要多加一个参数,如下:
- # 新增一个参数order,默认为20
- def page_url(content, order = 20, beg = 0):
- try:
- url = []
- i = 0
- while i < order:
- url1 = content.index('<h2><a href="',beg)+13
- url2 = content.index('" ',url1)
- url.append(content[url1:url2])
- beg = url2
- i = i + 1
- return url
- except ValueError:
- return url
下面这个方法是传入参数num(需要多少期),一页20期,返回每一期的url,代码如下:
- def get_order(num):
- # num代表获取的条目数量
- url_list = []
- page = num / 20
- order = num % 20 # 超出一整页的条目
- if num < 20: # 如果获取的条目数量少于20(一页20个),直接爬取第一页的num条
- url = 'http://bohaishibei.com/post/category/main'
- main_html = getHtml(url)
- clean_content = main_content(main_html)
- url_list = url_list + page_url(clean_content, num)
- for i in range(1, page+1): # 需这里需要尾巴
- url = 'http://bohaishibei.com/post/category/main/page/%d' % i # 爬取整页的条目
- main_html = getHtml(url)
- clean_content = main_content(main_html)
- url_list = url_list + page_url(clean_content) #获取整夜
- if (i == page)&(order > 0): # 爬到最后一页,如果有超出一页的条目则继续怕order条
- url = 'http://bohaishibei.com/post/category/main/page/%d' % (i+1)
- main_html = getHtml(url)
- clean_content = main_content(main_html)
- url_list = url_list + page_url(clean_content, order)
- #print len(page_url(clean_content, order))
- return url_list
下面开始gogogo
- order = get_order(21)
- for i in range(0, len(order)): #这个遍历列表太丑了,改了: for i in order
- html = getHtml(order[i])
- content_data = content(html)
- title_data = title(content_data)
- img_data = get_img(content_data)
- data_out(title_data, img_data)
ok了所有的代码都写完了
完整的代码我已经上传到我的github上了,地址为:https://github.com/521xueweihan/PySpider/blob/master/Spider.py
这里我在测试的时候有bug,因为该网站上有时候有的地方没有img的地址。如下图
我的代码也就跟着出问题了,因为我的title和img列表数量不一了,而列表长度我是以title的len()为准,结果就出现超出范围了。
这里记录一下,然后我要去除bug了。
ok啦,bug消除了。我改了img的匹配方法如下:
- def get_img(content,beg = 0):
- # 匹配图片的url
- # 思路是利用str.index()和序列的切片
- try:
- img_list = []
- while True:
- src1 = content.index('src=',beg)+4 # 这样写就可以匹配src="/"
- src2 = content.index('/></p>',src1)
- img_list.append(content[src1:src2])
- beg = src2
- except ValueError:
- return img_list
主函数:
- order = get_order(30) # get_order方法接受参数,抓取多少期的数据
- for i in order: # 遍历列表的方法
- html = getHtml(i)
- content_data = content(html)
- title_data = title(content_data)
- img_data = get_img(content_data)
- data_out(title_data, img_data)
爬下来的数据:
data.txt属性(共30期的数据):
终于写完了!
开始时间——9:14
写爬虫,吃饭,洗澡,休息了一会。
结束时间——21:02
呼,没有半途而废就知足了,感觉这样把写爬虫的流程走了一遍下次再写的话会快一些吧。
爬虫是写完了,但是用网站显示还没有写,明天看如果没事就把网站写出来。
图片下载的功能,我还没有写,等写网站的时候再把它完善出来。
总结:
整个过程,纯手写,没有参考别人的代码。这一点可以赞一下。
这次写爬虫就是强制自己不用正则表达式,和XPATH,发现有很多地方,用这两个会很方便。这让我下定决心去学正则表达式和Xpath,哈哈。体会过才有深有感触。
下一个目标是学习正则表达式和Xpath。一点点来,当我学完就来写爬虫v2.0,逐步完善吧,如果上来就要写难得,我的智商着急啊!
然后多看看别人的爬虫,学习别人厉害的地方,提高自己。
欢迎大家指导交流。
完整的代码我已经上传到我的github上了,地址为:https://github.com/521xueweihan/PySpider/blob/master/Spider.py
[Python]新手写爬虫全过程(已完成)的更多相关文章
- [Python]新手写爬虫全过程(转)
今天早上起来,第一件事情就是理一理今天该做的事情,瞬间get到任务,写一个只用python字符串内建函数的爬虫,定义为v1.0,开发中的版本号定义为v0.x.数据存放?这个是一个练手的玩具,就写在tx ...
- [python]新手写爬虫v2.5(使用代理的异步爬虫)
开始 开篇:爬代理ip v2.0(未完待续),实现了获取代理ips,并把这些代理持久化(存在本地).同时使用的是tornado的HTTPClient的库爬取内容. 中篇:开篇主要是获取代理ip:中篇打 ...
- (转)Python新手写出漂亮的爬虫代码2——从json获取信息
https://blog.csdn.net/weixin_36604953/article/details/78592943 Python新手写出漂亮的爬虫代码2——从json获取信息好久没有写关于爬 ...
- (转)Python新手写出漂亮的爬虫代码1——从html获取信息
https://blog.csdn.net/weixin_36604953/article/details/78156605 Python新手写出漂亮的爬虫代码1初到大数据学习圈子的同学可能对爬虫都有 ...
- Python urllib2写爬虫时候每次request open以后一定要关闭
最近用python urllib2写一个爬虫工具,碰到运行一会程序后就会出现scoket connection peer reset错误.经过多次试验发现原来是在每次request open以后没有及 ...
- 为什么python适合写爬虫?(python到底有啥好的?!)
我用c#,java都写过爬虫.区别不大,原理就是利用好正则表达式.只不过是平台问题.后来了解到很多爬虫都是用python写的.因为目前对python并不熟,所以也不知道这是为什么.百度了下结果: 1) ...
- (转)新手写爬虫v2.5(使用代理的异步爬虫)
开始 开篇:爬代理ip v2.0(未完待续),实现了获取代理ips,并把这些代理持久化(存在本地).同时使用的是tornado的HTTPClient的库爬取内容. 中篇:开篇主要是获取代理ip:中篇打 ...
- [转]让你从零开始学会写爬虫的5个教程(Python)
让你从零开始学会写爬虫的5个教程(Python) 写爬虫总是非常吸引IT学习者,毕竟光听起来就很酷炫极客,我也知道很多人学完基础知识之后,第一个项目开发就是自己写一个爬虫玩玩. 其实懂了之后,写个 ...
- 让你从零开始学会写爬虫的5个教程(Python)
写爬虫总是非常吸引IT学习者,毕竟光听起来就很酷炫极客,我也知道很多人学完基础知识之后,第一个项目开发就是自己写一个爬虫玩玩. 其实懂了之后,写个爬虫脚本是很简单的,但是对于新手来说却并不是那么容易. ...
随机推荐
- SQLServer中的死锁的介绍
简介 什么是死锁? 我认为,死锁是由于两个对象在拥有一份资源的情况下申请另一份资源,而另一份资源恰好又是这两对象正持有的,导致两对象无法完成操作,且所持资源无法释放. 什么又是阻塞? 阻塞是 ...
- 将一列包含多个ID拆分多行
看到个不常见的问题~然后在 Inner Sql Server2008 里面找到一个思路. 如果下面的表结构,如何拆分多行并对应员工号呢? 首先创建测试表 CREATE TABLE Department ...
- innobackupex --rsync 报错 Error: can't create file (null)/xtrabackup_rsyncfiles_pass1
在使用最新版的innobackupex(2.3.2): innobackupex /backup --rsync --user=xx --password=xxx 备份时报错: Error: can' ...
- InnoDB源码分析--事务日志(二)
原创文章,转载请标明原文链接:http://www.cnblogs.com/wingsless/p/5708992.html 昨天写了有关事务日志的一些基本点(http://www.cnblogs.c ...
- Windows环境下载与安装JBOSS服务器的详细图文教程
一.JDK的安装 首先安装JDK,配置环境变量(PATH,CLASSPATH,JAVA_HOME). 可以参照:Windows环境下JDK安装与环境变量配置 二.Jboss的介绍 JBOSS是EJB的 ...
- 解决Qt在openSUSE上编译出现“cannot find -lGL”错误
在openSUSE上编译QT5.4程序出现“cannot find -lGL”,就连example都无法通过编译.QT是在官网下的最新的安装包. 大体意思是,缺少qt运行时所需要的openGL库.决绝 ...
- WEB安全--逻辑漏洞
业务逻辑问题是一种设计缺陷.逻辑缺陷表现为设计者或开发者在思考过程中做出的特殊假设存在明显或隐含的错误.精明的攻击者会特别注意目标应用程序采用的逻辑方式,设法了解设计者与开发者做出的可能假设,然后考虑 ...
- AC日记——与7无关的数 openjudge 1.5 39
39:与7无关的数 总时间限制: 1000ms 内存限制: 65536kB 描述 一个正整数,如果它能被7整除,或者它的十进制表示法中某一位上的数字为7,则称其为与7相关的数.现求所有小于等于n( ...
- .NET(C#) File类的常用方法
MSDN的File类 File类在MSDN:https://msdn.microsoft.com/zh-cn/library/system.io.file_methods(v=vs.110).aspx ...
- nmap脚本扫描使用总结
nmap的脚本默认目录为:/usr/share/nmap/scripts/ Nmap提供的命令行参数如下 -sC: 等价于--script=default,使用默认类别的脚本进行扫描 可更换其他类别 ...