设为首页 收藏本站
查看: 3394|回复: 0

[经验分享] 用python抓取oj题目(2)——Sqlalchemy将数据存到数据库

[复制链接]

尚未签到

发表于 2015-4-21 09:21:17 | 显示全部楼层 |阅读模式
  额,早睡还不能早起,周六都要补课、、、
  上一篇用BS分析好界面元素之后,将我们需要的信息放到一个info的list里面给返回来出来,方便期间,info包括这些东西



##        """ return 12 infos
##        1.title 2.limit des 3.problem des 4.input 5.output
##        6.sample input 7.sample output 8.hint 9.author
##        10.source 11.recommend 12.imgages
##        the last element is a list of images """
  info的最后一个元素是一个图片的list。
  现在那,有了这些个信息,就需要把他们存到数据库里了。额,想了一下,图片的地址就不存来,直接把图片下载下来,然后通过html来引用就好了。所以,需要记录info里面的前11条信息,以及1个题号。

  先上代码(需要导入的东西忘了粘了from sqlalchemy import *        from sqlalchemy.orm import * ):



1 def store(start, end, url='http://acm.hdu.edu.cn/showproblem.php?pid='):
2     engine = create_engine("mysql://root:duoduo@localhost:3306/test?charset=utf8", encoding="utf-8", echo=True)
3     metadata = MetaData()
4     hdoj_table = Table('hdoj', metadata,
5             Column('problem_id', Integer, primary_key=True),
6             Column('title', String(255), nullable=False),
7             Column('limit_description', String(255), nullable=False),
8             Column('problem_description', Text, nullable=False),
9             Column('input', Text, nullable=False),
10             Column('output', Text, nullable=False),
11             Column('sample_input', Text, nullable=False),
12             Column('sample_output', Text, nullable=False),
13             Column('hint', Text, nullable=True),
14             Column('author', String(40), nullable=True),
15             Column('source', Text, nullable=True),
16             Column('recommend', String(255), nullable=True),
17             )
18
19     class Hdoj(object):
20         def __init__(self, problem_id, title, limit_description, problem_description, input, output, sample_input, sample_output, hint, author, source, recommend):                        
21             self.problem_id = problem_id
22             self.title = title
23             self.limit_description = limit_description
24             self.problem_description = problem_description
25             self.input = input
26             self.output = output
27             self.sample_input = sample_input
28             self.sample_output = sample_output
29             self.hint = hint
30             self.author = author
31             self.source = source
32             self.recommend = recommend
33         
34         def __repr__(self):
35             return "" % (self, title)
36     
37     metadata.create_all(engine)
38     
39     mapper(Hdoj, hdoj_table)
40     
41     Session = sessionmaker(autoflush=True, bind=engine)
42     
43     session = Session()
44     
45     data = []
46     images = []
47     for i in range(start, end):
48         problem_id = str(i)
49         info = catch(url + problem_id)
50         if info[2] == 'None':
51             continue
52         data.append(Hdoj(problem_id, info[0], info[1], info[2], info[3], info[4], info[5], info[6], info[7], info[8], info[9], info[10]))
53         images.append(info[-1])
54         
55            
56     length = len(data)
57     for i in range(length):
58         try:
59             session.add(data)
60             session.flush()
61             print 'adding'
62         except Exception as e:
63             #print 'exception'
64             #print e
65             session.rollback()
66             pass
67     return images
  
  解释:

1 def store(start, end, url='http://acm.hdu.edu.cn/showproblem.php?pid='):
    start和end是开始到结束的题号,例如我想把1000~2000题都给存下来,那么就store(1000,2000)就好了,url就是之前分析过的默认题目前缀。

2     engine = create_engine("mysql://root:123@localhost:3306/test?charset=utf8", encoding="utf-8", echo=True)
    sqlalchemy里面的,sqlalchemy那就像是一个抽象出来的数据库基类一样的东西,你只需要建立一个数据库的引擎,然后就可以在这个数据库上操作,具体的数据库的一些建表插入操作都不用你来操心了,非常方便。例如,我这个引擎是mysql的,用户名为root,密码123,字符集utf8,echo是为了显示一下sqlalchemy到底干了什么,所以设定为True,当然正式发布的时候需要改成False。


1     metadata = MetaData()
2     hdoj_table = Table('hdoj', metadata,
3             Column('problem_id', Integer, primary_key=True),
4             Column('title', String(255), nullable=False),
5             Column('limit_description', String(255), nullable=False),
6             Column('problem_description', Text, nullable=False),
7             Column('input', Text, nullable=False),
8             Column('output', Text, nullable=False),
9             Column('sample_input', Text, nullable=False),
10             Column('sample_output', Text, nullable=False),
11             Column('hint', Text, nullable=True),
12             Column('author', String(40), nullable=True),
13             Column('source', Text, nullable=True),
14             Column('recommend', String(255), nullable=True),
15             )

  metadata是神马我不是很了解,(元数据??),总之先记下来用着,然后就是新建一个表,每个oj一张表,所以,就有了上面这2~15行,单词描述的比较明显,不解释了就。



1 class Hdoj(object):
2         def __init__(self, problem_id, title, limit_description, problem_description, input, output, sample_input, sample_output, hint, author, source, recommend):                        
3             self.problem_id = problem_id
4             self.title = title
5             self.limit_description = limit_description
6             self.problem_description = problem_description
7             self.input = input
8             self.output = output
9             self.sample_input = sample_input
10             self.sample_output = sample_output
11             self.hint = hint
12             self.author = author
13             self.source = source
14             self.recommend = recommend
15         
16         def __repr__(self):
17             return "" % (self, title)
18     
19     metadata.create_all(engine)
20     
21     mapper(Hdoj, hdoj_table)
  接下来新建一个Hdoj的类,并且讲这个类与刚刚建的数据表做好映射。
  1~14行就是Hdoj了
  19、20行里面,是sqlalchemy的东西了,19行是真正的建立引擎,20行是将类与的数据库的表做映射。
  接下来是存储:



1     Session = sessionmaker(autoflush=True, bind=engine)
2     
3     session = Session()
4     
5     data = []
6     images = []
7     for i in range(start, end):
8         problem_id = str(i)
9         info = catch(url + problem_id)
10         if info[2] == 'None':
11             continue
12         data.append(Hdoj(problem_id, info[0], info[1], info[2], info[3], info[4], info[5], info[6], info[7], info[8], info[9], info[10]))
13         images.append(info[-1])
14         
15            
16     length = len(data)
17     for i in range(length):
18         try:
19             session.add(data)
20             session.flush()
21             print 'adding'
22         except Exception as e:
23             #print 'exception'
24             #print e
25             session.rollback()
26             pass
27     return images
  
  session这个东西,个人的理解就是一次链接,在这儿一个session就是python和数据库的一次链接,然后我们对这个session进行一系列操作,存储啊添加啊神马的,注意,只要session没有flush(),操作都是在缓存里面进行的,查文档发现commit()是提交的一个函数,它里面是会调用flush()的。

1     Session = sessionmaker(autoflush=True, bind=engine)  
  用sessionmaker建立一个Session,绑定了之前建立的引擎。
3     session = Session()   
  建立一个Session(你用sessionmaker建立的特殊的一个链接)的对象。
  5~13都是添加对象

8         problem_id = str(i)  
9         info = catch(url + problem_id)
  用之前写好的catch函数抓取需要的信息,存到info里面,注意url变了,这样来进行题目的遍历。
10         if info[2] == 'None':
11             continue
  这两行是防止神马的,防止数据库加入没有的题目,比如杭电的题目就到4186,如果store(10000,10010),会把着些没有的url给存到数据库里面,这不是我们想要的,如果没有这个题目的话,让他默默的失败,进行下一个链接。
  

5     data = []
6     images = [] 
12         data.append(Hdoj(problem_id, info[0], info[1], info[2], info[3], info[4], info[5], info[6], info[7], info[8], info[9], info[10]))
13         images.append(info[-1])
  data是我真正想存到数据库里面的信息,images则是不想存到里面的图片信息,分开分开。



1     length = len(data)
2     for i in range(length):
3         try:
4             session.add(data)
5             session.flush()
6             print 'adding'
7         except Exception as e:
8             #print 'exception'
9             #print e
10             session.rollback()
11             pass
12     return images
  
  这几行是最重要,这是真正把数据放到数据库里的。
  首先要说明为什马要用异常,我的主键用的是题号啊,第一次store(1000,1120)的时候一般是不会错的,如果第二次我想store(1100,1200),数据库会告诉我主键重复,不能添加,额,这个当时纠结了好久,各种查,无果,学长告诉我说你可以抓异常啊,弱弱的说一句,我这还真是第一次用异常,没想到介么好用、、
  然后是rollback(),rollback像他的名字一样,回滚。session在进行一次链接的时候,每条数据和数据库链接在一起,直到等到rollback它才进行下一条数据的链接(这都是我自己感觉的,如果不对啊话@我下下,学习中),额、怎么说那,举个例子吧,如果说我存store(1055,1070),在1056题,4行添加数据,5行想要更新缓存,写入数据库的时候出现异常了,假设没有rollback这个函数,仅仅是pass的话,session着个叫做链接的东西依然把python和数据库里面的1056进行链接,而不会向下走,结果就是下面的14条数据继续异常,而且给的异常都是第1056题目的主键重复,就是说在循环继续后,数据库并没有断开和1056题的链接,依然尝试把1056题的数据加进来。(这些是真的啊,就不回去实验截图了,一会儿还要上课)
  信息要一条一条的存,包不准那一条就会有异常。虽然说session有个addAll()函数,但是如果里面有一条数据出了异常,如果用我第10行的处理机制,那么那一条异常数据后面的所有数据全部都不能添加了。
  由于不想把图片存到数据库里面,把它直接return出去了,新写一个函数把图片存到本地。
  就是用python处理文件的那一套,代码:



1 def download_img(url, default_url_head='http://acm.hdu.edu.cn/data/images/', default_file_head='/home/duoduo/images/', pro_file_head='hdoj'):
2     filename = url.split('/')[-1]
3     url_head = default_url_head
4     file_head = default_file_head
5     
6     url_path = url_head + filename
7     #print url_path
8     file_path = file_head + pro_file_head + '/' + filename
9     #print file_path
10     try:
11         open_img = urllib2.urlopen(url_path)
12         img = open_img.read()
13         img_file = open(file_path, 'wb')
14         img_file.write(img)
15         img_file.close()
16         print 'downloading images from:' + url_path
17     except:
18         print 'HTTP Error 404: Not Found'
19         pass
  第2~9行就是把url和本地的路径进行一些处理
  10~19行下载,依然要抓异常,因为有些图本身就挂了,打不开,会弹出404异常
  13~15就是文件的读写操作,把图片信息存起来来。
  
  存储就到这里了,然后包一下,做一个start函数,把上面的东西都给装进来:



1 def start(begin, end):
2     if(end < begin):
3         print '输入有误,请检查您的输入。'
4         return None
5
6     default_group_num = 50
7     groups = (end - begin) / default_group_num
8     
9     for i in range(groups):
10         group_start = i * default_group_num + begin
11         group_end = (i + 1) * default_group_num + begin
12         images_list = store(group_start, group_end)
13         len_img_list = len(images_list)
14         for i in range(len_img_list):                        
15             len_img = len(images_list)
16             for j in range(len_img):                    
17                 download_img(images_list[j])
18
19     if (end - begin) >= 0 :
20         last_group_start = groups * default_group_num + begin
21         images_list = store(last_group_start, end + 1)
22         len_img_list = len(images_list)
23         for i in range(len_img_list):                        
24             len_img = len(images_list)
25             for j in range(len_img):                    
26                 download_img(images_list[j])
6     default_group_num = 50
  默认一次存多少数据,我怕一次存太多,内存受不了,或者是等了10来分钟结果中途崩了,还要从头来,麻烦。
7     groups = (end - begin) / default_group_num
  groups以及一下的for循环就是做分组处理的。
  python抓数据,存数据就彻底ok了,下一步就是用django显示看看是否正确,django的静态文件这次是深刻体会了,上课去先,晚上或者明天在写下一篇吧。  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-59105-1-1.html 上篇帖子: 用Python和摄像头制作简单的延时摄影 下篇帖子: Python编程规范
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表