用python抓取oj题目(2)——Sqlalchemy将数据存到数据库


  上一篇http://www.2cto.com/kf/201204/125926.html 用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 "<Hdoj('%s')>" % (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[i])
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 "<Hdoj('%s')>" % (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[i])
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[i])
 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[i])
16             for j in range(len_img):                   
17                 download_img(images_list[i][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[i])
25             for j in range(len_img):                   
26                 download_img(images_list[i][j])

 6     default_group_num = 50
  默认一次存多少数据,我怕一次存太多,内存受不了,或者是等了10来分钟结果中途崩了,还要从头来,麻烦。
 7     groups = (end - begin) / default_group_num
  groups以及一下的for循环就是做分组处理的。
  python抓数据,存数据就彻底ok了,下一步就是用django显示看看是否正确,django的静态文件这次是深刻体会了,上课去先,晚上或者明天在写下一篇吧。  

 

摘自  duoduo3_69
 

相关内容

    暂无相关文章

评论关闭