├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── Makefile ├── README.rst ├── example.py ├── kb ├── __init__.py └── nl │ ├── __init__.py │ ├── api │ ├── __init__.py │ ├── oai.py │ └── sru.py │ ├── collections │ └── __init__.py │ └── helpers │ └── __init__.py ├── setup.py └── test ├── alto_to_text_test.py ├── oai_test.py ├── sru_test.py └── test_data └── anp_1937-10-01_1_alto.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | kb.egg-info 3 | __pycache__ 4 | build 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.7" 4 | # command to install dependencies 5 | install: 6 | - "pip3 install ." 7 | # command to run tests 8 | script: make test 9 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: install 2 | 3 | doc: 4 | rst2html README.rst > README.html 5 | 6 | test: install testENV testOAI testSRU testALTOtoTXT 7 | 8 | testENV: 9 | python3 setup.py install 10 | 11 | testOAI: 12 | python3 ./test/oai_test.py 13 | 14 | testSRU: 15 | python3 ./test/sru_test.py 16 | 17 | testALTOtoTXT: 18 | python3 ./test/alto_to_text_test.py 19 | 20 | clean: 21 | rm -rf build 22 | rm -rf dist 23 | rm -rf kb.egg-info 24 | find . -name \*.pyc -exec rm '{}' ';' 25 | find . -name "__pycache__" -exec rmdir {} \; 26 | rm README.html 27 | 28 | install: 29 | python3 setup.py install 30 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://upload.wikimedia.org/wikipedia/commons/thumb/1/17/Logo_Koninklijke_Bibliotheek_wordmark.svg/120px-Logo_Koninklijke_Bibliotheek_wordmark.svg.png 2 | :alt: Koninklijke Bibliotheek Logo 3 | :align: right 4 | :scale: 50 % 5 | :width: 100 px 6 | :height: 100 px 7 | 8 | 9 | .. _API: https://en.wikipedia.org/wiki/Application_programming_interface 10 | .. _DataServices: http://www.kb.nl/bronnen-zoekwijzers/dataservices-en-apis 11 | .. _Delpher: http://www.delpher.nl/ 12 | .. _KB: http://www.kb.nl/en 13 | .. _OAI-MPH: http://www.openarchives.org/pmh/ 14 | .. _Python: http://python.org/ 15 | .. _SRU: http://www.loc.gov/standards/sru/ 16 | .. _Travis: https://travis-ci.org/KBNLresearch/KB-python-API 17 | .. _CC-BY-NC-ND: https://creativecommons.org/licenses/by-nc-nd/2.0/ 18 | .. _Pypi: https://pypi.python.org/pypi/kb/ 19 | .. _Downloads: https://pepy.tech/project/kb 20 | 21 | ===================================================================== 22 | KB python API: Access to National Library of the Netherlands datasets 23 | ===================================================================== 24 | .. image:: https://pepy.tech/badge/kb 25 | :alt: Downloads 26 | :align: left 27 | .. image:: https://img.shields.io/pypi/v/kb?logo=kb 28 | :alt: Pypi version 29 | :align: left 30 | Pypi_ version 31 | 32 | .. image:: https://api.travis-ci.org/KBNLresearch/KB-python-API.svg 33 | :alt: build status 34 | :align: left 35 | Travis_ build status 36 | 37 | KB-Python-API is a simple API_ for Python_, the API provides easy access to free and CC-BY-NC-ND_ datasets provided by the National Library of the Netherlands (KB_). 38 | 39 | It relies on the back-end infrastructure of the KB_ which consists of an SRU_ and OAI-MPH_ service. The KB Python API makes it easy to interact with historical data, 40 | for more information on the provided datasets and data-rights take a look at the DataServices_ page of the KB. 41 | 42 | For example usage have a look at the provided example.py file, or consult the /test directory. 43 | 44 | This package is also available from the pypi_ website. 45 | To do a quick install: 46 | 47 | .. code-block:: python 48 | pip install kb 49 | 50 | 51 | OAI example 52 | =========== 53 | .. code-block:: python 54 | 55 | >>> from kb.nl.api import oai 56 | >>> from kb.nl.helpers import alto_to_text 57 | >>> oai.list_sets() 58 | ['ANP', 'BYVANCK', 'DPO', 'SGD'] 59 | >>> records = oai.list_records("ANP") 60 | >>> records.identifiers[:3] 61 | ['anp:1937:10:01:1', 'anp:1937:10:01:2', 'anp:1937:10:01:3'] 62 | >>> len(oai.resumptiontoken) 63 | 42 64 | >>> record = oai.get(records.identifiers[0]) 65 | >>> alto_record = record.alto 66 | >>> alto_to_text(alto_record[0]).split("\\n")[1][:27] 67 | u' RADIO 1 van 1 Ootober 1937' 68 | >> image_record = record.image 69 | >>> len(image_record) 70 | 721035 71 | 72 | SRU example 73 | =========== 74 | .. code-block:: python 75 | 76 | >>> from kb.nl.api import sru 77 | >>> from kb.nl.helpers import alto_to_text 78 | >>> response = sru.search("Beatrix AND Juliana AND Bernhard AND telegram", "ANP") 79 | >>> for record in response.records: 80 | ... print("Date: %s" % record.date) 81 | ... print("Abstract: %s" % record.abstract) 82 | ... print("Title: %s" % record.title) 83 | -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | 6 | from kb.nl.api import sru 7 | from kb.nl.api import oai 8 | from kb.nl.helpers import alto_to_text 9 | 10 | oai_handler = oai 11 | oai_handler.current_set = "ANP" 12 | oai_handler.set_key("") 13 | 14 | response = sru.search("karel AND reve AND hooftprijs", "ANP") 15 | print ("Number of records: %i" % response.sru.nr_of_records) 16 | 17 | record_nr = 0 18 | 19 | for record in response.records: 20 | record_nr += 1 21 | print("********~ Record number %i ~*********" % record_nr) 22 | print("Date: %s" % record.dates) 23 | print("RecordIdentifier: %s" % record.identifiers) 24 | print("Abstract: %s" % record.abstracts) 25 | print("Title: %s" % record.titles) 26 | oai_handler.DEBUG = True 27 | r = oai_handler.get(record.identifiers[0]) 28 | for alto in r.alto: 29 | print("Fulltext: %s" % alto_to_text(alto)) 30 | -------------------------------------------------------------------------------- /kb/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.1.5' 2 | __all__ = ['nl'] 3 | 4 | -------------------------------------------------------------------------------- /kb/nl/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.1.5' 2 | __all__ = ['api', 'collections', 'helpers'] 3 | -------------------------------------------------------------------------------- /kb/nl/api/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.1.5' 2 | 3 | __all__ = ['oai', 'sru'] 4 | __author__ = 'WillemJan Faber -1] 176 | return ids 177 | 178 | @property 179 | def deleted_identifiers(self): 180 | """ 181 | Return al list of deleted record Identifiers. 182 | """ 183 | deleted = [] 184 | for item in self.records_data[2]: 185 | if item.tag.endswith('record'): 186 | if item[0].tag.endswith('header') and \ 187 | item[0].attrib.get('status') == 'deleted': 188 | deleted.append(item[0][0].text) 189 | return deleted 190 | 191 | 192 | class record(): 193 | 194 | """ 195 | Class for parsing XML output from OAI server, 196 | to more human readable form. This class 197 | is a generic record level object. 198 | 199 | A record has a bunch of properties, most of them 200 | are exposed, for example: 201 | 202 | >>> record.ocr # Returns the ocr for a record. 203 | 204 | >>> record.alto # Returns the alto files for a record. 205 | 206 | >>> record.image # Returns the image for a record. 207 | # Mostly .jpg's 208 | """ 209 | record_data = False 210 | DEBUG = False 211 | current_set = False 212 | 213 | def __init__(self, record_data, current_set=False, debug=False): 214 | """ 215 | :param record_data: XML object of the wanted record. 216 | :param debug: enable or disable debuging 217 | """ 218 | self.record_data = record_data 219 | self.current_set = current_set 220 | 221 | if debug: 222 | self.DEBUG = debug 223 | 224 | @property 225 | def alto(self): 226 | """ 227 | Return the ALTO(s) for the current record. 228 | For more information on ALTO see: 229 | 230 | https://en.wikipedia.org/wiki/ALTO_(XML) 231 | 232 | For parsing alto data back to text, use: 233 | 234 | >>> from kb.nl.helpers import alto_to_text 235 | >>> alto_to_text(alto) 236 | """ 237 | if self.current_set == "BYVANCK": 238 | return False 239 | 240 | alto_url_list = [] 241 | alto_url = False 242 | 243 | for item in self.record_data.iter(): 244 | if item.attrib and \ 245 | item.attrib.get('ref') and \ 246 | item.attrib['ref'].lower().endswith(':alto'): 247 | 248 | alto_url = item.attrib['ref'] 249 | 250 | if alto_url not in alto_url_list: 251 | alto_url_list.append(alto_url) 252 | 253 | if not alto_url: 254 | for item in self.record_data.iter(): 255 | if item.attrib and \ 256 | item.attrib.get('ref') and \ 257 | item.attrib['ref'].lower().endswith('.xml'): 258 | 259 | alto_url = item.attrib['ref'] 260 | alto_url_list.append(alto_url) 261 | break 262 | 263 | # The result is either one ALTO file, 264 | # or a bunch of them, if more then one, 265 | # fetch the results and return a list. 266 | if len(alto_url_list) == 0: 267 | return False 268 | 269 | elif len(alto_url_list) <= 1: 270 | alto_url = alto_url_list[0] 271 | 272 | if self.DEBUG: 273 | sys.stdout.write(alto_url) 274 | 275 | response = requests.get(alto_url) 276 | 277 | if not response.status_code == 200: 278 | raise Exception('Error while getting data from %s' % alto_url) 279 | 280 | return [response.text] 281 | else: 282 | alto_list = [] 283 | 284 | for alto_url in alto_url_list: 285 | response = requests.get(alto_url) 286 | 287 | if self.DEBUG: 288 | sys.stdout.write(alto_url + " ") 289 | 290 | if not response.status_code == 200: 291 | raise Exception( 292 | 'Error while getting data from %s' % alto_url) 293 | 294 | alto_list.append(response.text) 295 | 296 | return alto_list 297 | 298 | @property 299 | def ocr(self): 300 | """ 301 | Return the OCR for the current record. 302 | 303 | The OCR was produced with Abby-Finereader. 304 | """ 305 | for item in self.record_data.iter(): 306 | if item.attrib and \ 307 | item.attrib.get('ref') and \ 308 | item.attrib['ref'].lower().endswith(':ocr'): 309 | url = item.attrib['ref'] 310 | if self.DEBUG: 311 | sys.stdout.write(url) 312 | 313 | response = requests.get(url) 314 | if not response.status_code == 200: 315 | return False 316 | return response.text 317 | return False 318 | 319 | @property 320 | def image(self): 321 | """ 322 | Retrieve the image for the current record, 323 | and return the bits. 324 | """ 325 | img_url = False 326 | 327 | if self.current_set == "BYVANCK": 328 | img_url = 'http://imageviewer.kb.nl/' 329 | img_url += 'ImagingService/imagingService?id=' 330 | img_url += self.record_data[2][0][1][0][1].text.split('=')[1] 331 | else: 332 | for item in self.record_data.iter(): 333 | if item.attrib and \ 334 | item.attrib.get('ref') and \ 335 | item.attrib.get('ref').startswith('http://') and \ 336 | (item.attrib['ref'].endswith(':image') or 337 | item.attrib['ref'].endswith('.jpg')): 338 | 339 | img_url = item.attrib['ref'] 340 | break 341 | 342 | if not img_url: 343 | return False 344 | 345 | if self.DEBUG: 346 | sys.stdout.write(img_url) 347 | 348 | response = requests.get(img_url) 349 | 350 | if not response.status_code == 200: 351 | raise Exception('Error while getting data from %s' % img_url) 352 | 353 | return response.content 354 | 355 | @property 356 | def title(self): 357 | """ 358 | Get corresponding title. 359 | """ 360 | for item in self.record_data.iter(): 361 | if item.tag.endswith('title'): 362 | return item.text 363 | return False 364 | 365 | @property 366 | def annotation(self): 367 | """ 368 | Get corresponding annotation. 369 | """ 370 | for item in self.record_data.iter(): 371 | if item.tag.endswith('annotation'): 372 | return item.text 373 | return False 374 | 375 | @property 376 | def date(self): 377 | """ 378 | Get timeperiod of creation. 379 | """ 380 | for item in self.record_data.iter(): 381 | if item.tag.endswith('date'): 382 | return item.text 383 | return False 384 | 385 | @property 386 | def creator(self): 387 | """ 388 | Get corresponding creator. 389 | """ 390 | for item in self.record_data.iter(): 391 | if item.tag.endswith('creator'): 392 | return item.text 393 | return False 394 | 395 | @property 396 | def contributor(self): 397 | """ 398 | Get contributors to this record. 399 | """ 400 | for item in self.record_data.iter(): 401 | if item.tag.endswith('contributor'): 402 | return item.text 403 | return False 404 | 405 | @property 406 | def publisher(self): 407 | """ 408 | Get publisher information for this record. 409 | """ 410 | for item in self.record_data.iter(): 411 | if item.tag.endswith('publisher'): 412 | return item.text 413 | return False 414 | -------------------------------------------------------------------------------- /kb/nl/api/sru.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import requests 3 | import urllib 4 | import pprint 5 | 6 | try: 7 | from urllib import quote # Python 2.X 8 | except ImportError: 9 | from urllib.parse import quote # Python 3+ 10 | 11 | from kb.nl.collections import SETS 12 | from kb.nl.helpers import etree 13 | 14 | SRU_BASEURL = 'https://jsru.kb.nl/sru/sru' 15 | SRU_BASEURL += '?version=1.2&maximumRecords=%i' 16 | SRU_BASEURL += '&operation=searchRetrieve' 17 | SRU_BASEURL += '&startRecord=%i' 18 | SRU_BASEURL += '&recordSchema=%s' 19 | SRU_BASEURL += '&x-collection=%s&query=%s' 20 | 21 | 22 | class response(): 23 | def __init__(self, record_data, sru): 24 | self.record_data = record_data 25 | self.sru = sru 26 | 27 | @property 28 | def records(self): 29 | if self.sru.nr_of_records == 0: 30 | record_data = "" 31 | else: 32 | ns = {'zs': 'http://www.loc.gov/zing/srw/'} 33 | record_data = self.record_data.xpath("zs:records/zs:record", 34 | namespaces=ns)[0] 35 | return(record(record_data, self.sru)) 36 | 37 | # TODO: distinguish by xsi:type 38 | @property 39 | def identifiers(self): 40 | baseurl = 'https://resolver.kb.nl/resolve?urn=' 41 | result = [r.text.replace(baseurl, '') for r in self.record_data.iter() if 42 | r.tag.endswith('identifier') and r.text.find(':') > -1] 43 | return result 44 | 45 | @property 46 | def types(self): 47 | return [r.text for r in self.record_data.iter() if 48 | r.tag.endswith('type')] 49 | 50 | @property 51 | def languages(self): 52 | return [r.text for r in self.record_data.iter() if 53 | r.tag.endswith('language')] 54 | 55 | @property 56 | def dates(self): 57 | return [r.text for r in self.record_data.iter() if 58 | r.tag.endswith('date')] 59 | 60 | @property 61 | def extents(self): 62 | return [r.text for r in self.record_data.iter() if 63 | r.tag.endswith('extent')] 64 | 65 | @property 66 | def creators(self): 67 | return [r.text for r in self.record_data.iter() if 68 | r.tag.endswith('creator')] 69 | 70 | @property 71 | def contributors(self): 72 | return [r.text for r in self.record_data.iter() if 73 | r.tag.endswith('contributor')] 74 | 75 | # TODO: distinguish by xsi:type and xml:lang 76 | @property 77 | def subjects(self): 78 | return [r.text for r in self.record_data.iter() if 79 | r.tag.endswith('subject')] 80 | 81 | @property 82 | def abstracts(self): 83 | return [r.text for r in self.record_data.iter() if 84 | r.tag.endswith('abstract')] 85 | 86 | @property 87 | def titles(self): 88 | return [r.text for r in self.record_data.iter() if 89 | r.tag.endswith('title')] 90 | 91 | @property 92 | def publishers(self): 93 | return [r.text for r in self.record_data.iter() if 94 | r.tag.endswith('publisher')] 95 | 96 | # Following properties occur in GGC 97 | 98 | @property 99 | def annotations(self): 100 | return [r.text for r in self.record_data.iter() if 101 | r.tag.endswith('annotation')] 102 | 103 | 104 | class record(): 105 | def __init__(self, record_data, sru): 106 | self.record_data = record_data 107 | self.sru = sru 108 | 109 | def __iter__(self): 110 | return self 111 | 112 | def __next__(self): 113 | if self.sru.nr_of_records == 0: 114 | raise StopIteration 115 | if self.sru.startrecord < self.sru.nr_of_records + 1: 116 | record_data = self.sru.run_query() 117 | self.sru.startrecord += 1 118 | return response(record_data, self.sru) 119 | else: 120 | raise StopIteration 121 | 122 | def next(self): 123 | return self.__next__() 124 | 125 | 126 | class sru(): 127 | DEBUG = True 128 | 129 | collection = False 130 | maximumrecords = 50 131 | nr_of_records = 0 132 | query = "" 133 | recordschema = False 134 | sru_collections = SETS 135 | startrecord = 0 136 | 137 | def search(self, query, collection=False, 138 | startrecord=1, maximumrecords=1, recordschema=False): 139 | 140 | self.maximumrecords = maximumrecords 141 | self.query = quote(query) 142 | self.startrecord = startrecord 143 | 144 | if collection not in self.sru_collections: 145 | raise Exception('Unknown collection') 146 | 147 | self.collection = self.sru_collections[collection]['collection'] 148 | 149 | if not self.collection: 150 | raise Exception('Error, no collection specified') 151 | 152 | if not recordschema: 153 | self.recordschema = self.sru_collections[collection]['recordschema'] 154 | else: 155 | self.recordschema = recordschema 156 | 157 | record_data = self.run_query() 158 | 159 | nr_of_records = [i.text for i in record_data.iter() if 160 | i.tag.endswith('numberOfRecords')][0] 161 | 162 | self.nr_of_records = int(nr_of_records) 163 | 164 | if self.nr_of_records > 0: 165 | return response(record_data, self) 166 | 167 | return False 168 | 169 | def run_query(self): 170 | url = SRU_BASEURL % (self.maximumrecords, self.startrecord, 171 | self.recordschema, self.collection, self.query) 172 | if self.DEBUG: 173 | print("run_query: %s" % url) 174 | 175 | r = requests.get(url) 176 | 177 | if not r.status_code == 200: 178 | raise Exception('Error while getting data from %s' % url) 179 | 180 | record_data = etree.fromstring(r.content) 181 | 182 | return record_data 183 | -------------------------------------------------------------------------------- /kb/nl/collections/__init__.py: -------------------------------------------------------------------------------- 1 | SETS = {'ANP': {'collection': 'ANP', 2 | 'description_en': 'Radio Bulletins ANP Press Agency', 3 | 'description_nl': 'ANP Radiobulletins Digitaal', 4 | 'metadataPrefix': 'didl', 5 | 'recordschema': 'dcx', 6 | 'setname': 'anp', 7 | 'time_period': [1937, 1989]}, 8 | 'DPO': {'collection': 'DPO_boekdeel', 9 | 'description_en': 'Early Dutch Books Online', 10 | 'description_nl': 'Early Dutch Books Online', 11 | 'metadataPrefix': 'didl', 12 | 'recordschema': 'ddd', 13 | 'setname': 'DPO', 14 | 'time_period': [1781, 1800]}, 15 | 'BYVANCK': {'collection': 'MISC', 16 | 'description_en': 'Medieval Illuminated Manuscripts', 17 | 'description_nl': 'Middeleeuwse Verluchte Handschriften', 18 | 'metadataPrefix': 'dcx', 19 | 'recordschema': 'dcx', 20 | 'setname': 'BYVANCK', 21 | 'time_period': [500, 1500], 22 | 'extra_query' : '(dcterms:isPartOf="ByvanckB" OR dcterms:isPartOf="BYVANCK")'}, 23 | 'SGD': {'collection': 'SGD', 24 | 'description_en': 'States General Digital', 25 | 'description_nl': 'Staten-Generaal Digitaal', 26 | 'metadataPrefix': 'dcx', 27 | 'recordschema': 'dcx', 28 | 'setname': 'sgd:register', 29 | 'time_period': [1962, 1994]}, 30 | 'GGC': {'collection': 'GGC', 31 | 'description_en': 'General Catalogue KB', 32 | 'description_nl': 'Algemene Catalogus KB', 33 | 'metadataPrefix': 'dcx', 34 | 'recordschema': 'dcx', 35 | 'setname': 'ggc', 36 | 'time_period': [1937, 2016]} 37 | } 38 | -------------------------------------------------------------------------------- /kb/nl/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | try: 2 | from lxml import etree 3 | except ImportError: 4 | try: 5 | # Python 2.5 6 | import xml.etree.cElementTree as etree 7 | except ImportError: 8 | try: 9 | # Python 2.5 10 | import xml.etree.ElementTree as etree 11 | except ImportError: 12 | try: 13 | # normal cElementTree install 14 | import cElementTree as etree 15 | except ImportError: 16 | try: 17 | # normal ElementTree install 18 | import elementtree.ElementTree as etree 19 | except ImportError: 20 | raise("Failed to import ElementTree from any known place") 21 | import codecs 22 | 23 | def alto_to_text(alto_data): 24 | ''' Grab the selected text blocks and write them to disk ''' 25 | try: 26 | # some files have a BOM available which does not get stripped on windows 27 | if not alto_data[0] == '<': 28 | alto_data = alto_data[3:] 29 | alto_data = etree.fromstring(alto_data.encode('utf-8')) 30 | 31 | except etree.XMLSyntaxError as e: 32 | print ("error! %s" % alto_str[:40]) 33 | return None 34 | 35 | alto_text = u"" 36 | prev_was_hyp = False 37 | 38 | for item in alto_data.iter(): 39 | if item.tag.endswith("String"): 40 | if prev_was_hyp: 41 | alto_text += item.get("CONTENT") 42 | prev_was_hyp = False 43 | else: 44 | alto_text += u" " + item.get("CONTENT") 45 | 46 | if item.tag.endswith("HYP"): 47 | prev_was_hyp = True 48 | 49 | if item.tag.endswith("TextBlock"): 50 | if len(alto_text) > 0: 51 | alto_text += u"\n" 52 | 53 | return alto_text 54 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | author="WillemJan Faber", 5 | author_email="WillemJan.faber@kb.nl", 6 | name='kb', 7 | url="https://github.com/KBNLresearch/KB-python-API", 8 | description='Access to National Library of the Netherlands datasets', 9 | version='0.1.9', 10 | packages=['kb.nl.api', 'kb.nl.helpers', 'kb', 'kb.nl', 'kb.nl.collections'], 11 | license='GNU General Public License', 12 | install_requires=['lxml>=2.3', 'requests'], 13 | classifiers=['Development Status :: 3 - Alpha', 14 | 'Intended Audience :: Developers', 15 | 'Intended Audience :: Science/Research', 16 | 'Programming Language :: Python :: 3', 17 | 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', 18 | 'Natural Language :: Dutch', 19 | 'Topic :: Sociology :: History'] 20 | ) 21 | -------------------------------------------------------------------------------- /test/alto_to_text_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import sys 6 | 7 | sys.path.append('.') 8 | sys.path.append('..' + os.sep) 9 | 10 | 11 | def alto_to_text_test(): 12 | """ 13 | >>> import os 14 | >>> import codecs 15 | >>> from kb.nl.helpers import alto_to_text 16 | >>> path = os.path.dirname(os.path.abspath(__file__)) + os.sep 17 | >>> path += "test_data" + os.sep + "anp_1937-10-01_1_alto.xml" 18 | >>> fh = codecs.open(path, "r", encoding="utf-8") 19 | >>> data = fh.read() 20 | >>> fh.close() 21 | >>> alto_to_text(data).split('\\n')[2][:45] 22 | ' De Rijksradiocontroledienst heeft te UTRECHT' 23 | """ 24 | 25 | if __name__ == "__main__": 26 | import doctest 27 | doctest.testmod() 28 | -------------------------------------------------------------------------------- /test/oai_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | 6 | sys.path.append('..' + os.sep) 7 | sys.path.append('.' + os.sep) 8 | 9 | def oai_anp(): 10 | """ 11 | >>> from kb.nl.api import oai 12 | >>> from kb.nl.helpers import alto_to_text 13 | >>> oai.DEBUG=True 14 | >>> sorted(oai.list_sets()) 15 | ['ANP', 'BYVANCK', 'DPO', 'GGC', 'SGD'] 16 | >>> records = oai.list_records("ANP") 17 | http://services.kb.nl/mdo/oai?verb=ListRecords&metadataPrefix=didl&set=anp 18 | >>> records.identifiers[:3] 19 | ['anp:1937:10:01:1', 'anp:1937:10:01:2', 'anp:1937:10:01:3'] 20 | >>> len(oai.resumptiontoken) 21 | 42 22 | >>> record = oai.get(records.identifiers[0]) 23 | http://services.kb.nl/mdo/oai?verb=GetRecord&identifier=anp:anp:1937:10:01:1:mpeg21&metadataPrefix=didl 24 | >>> alto_records = record.alto 25 | http://resolver.kb.nl/resolve?urn=anp:1937:10:01:1:mpeg21:alto 26 | >>> len(alto_records[0]) 27 | 90124 28 | >>> alto_to_text(alto_records[0]).split("\\n")[1][:27] 29 | ' RADIO 1 van 1 Ootober 1937' 30 | >>> image_record = record.image 31 | http://resolver.kb.nl/resolve?urn=anp:1937:10:01:1:mpeg21:image 32 | >>> len(image_record) 33 | 721035 34 | >>> record.title 35 | 'ANP Nieuwsbericht - 01-10-1937 - 1' 36 | >>> record.date 37 | '1937-10-01' 38 | """ 39 | 40 | def oai_sgd(): 41 | """ 42 | >>> from kb.nl.api import oai 43 | >>> oai.DEBUG=True 44 | >>> records = oai.list_records("SGD") 45 | http://services.kb.nl/mdo/oai?verb=ListRecords&metadataPrefix=dcx&set=sgd:register 46 | >>> records.identifiers[:1] 47 | ['sgd:register:mpeg21:1967:0001424:L0000:lemma'] 48 | >>> len(oai.resumptiontoken) 49 | 50 50 | >>> record = oai.get(records.identifiers[0]) 51 | http://services.kb.nl/mdo/oai?verb=GetRecord&identifier=SGD:sgd:mpeg21:1967:0001424&metadataPrefix=didl 52 | >>> ocr_data = record.ocr # SGD does not have real alto files.. 53 | http://resolver.kb.nl/resolve?urn=sgd:1967:0003969:ocr 54 | >>> len(ocr_data) 55 | 4777 56 | """ 57 | 58 | 59 | def oai_dpo(): 60 | """ 61 | >>> from kb.nl.api import oai 62 | >>> from kb.nl.helpers import alto_to_text 63 | >>> oai.DEBUG=True 64 | >>> records = oai.list_records("DPO") 65 | http://services.kb.nl/mdo/oai?verb=ListRecords&metadataPrefix=didl&set=DPO 66 | >>> records.identifiers[:3] 67 | ['dpo:10221:mpeg21', 'dpo:10863:mpeg21', 'dpo:10864:mpeg21'] 68 | >>> len(oai.resumptiontoken) 69 | 42 70 | >>> records = oai.list_records("DPO") # doctest: +ELLIPSIS 71 | http://services.kb.nl/mdo/oai?verb=ListRecords&metadataPrefix=didl&set=DPO&resumptionToken... 72 | >>> records.identifiers[:3] 73 | ['dpo:10012:mpeg21', 'dpo:10014:mpeg21', 'dpo:10015:mpeg21'] 74 | >>> record = oai.get(records.identifiers[0]) 75 | http://services.kb.nl/mdo/oai?verb=GetRecord&identifier=DPO:dpo:10012:mpeg21&metadataPrefix=didl 76 | >>> alto_records = record.alto # doctest: +ELLIPSIS 77 | http://resolver.kb.nl/resolve?urn=dpo:10012:mpeg21:0001:alto http://... 78 | >>> len(alto_records[0]) 79 | 1739 80 | >>> record.title[:20] 81 | 'De minagting der her' 82 | >>> record.contributor 83 | False 84 | >>> record.publisher 85 | False 86 | """ 87 | 88 | 89 | def oai_byvanck(): 90 | """ 91 | >>> from kb.nl.api import oai 92 | >>> oai.DEBUG=True 93 | >>> records = oai.list_records("BYVANCK") 94 | http://services.kb.nl/mdo/oai?verb=ListRecords&metadataPrefix=dcx&set=BYVANCK 95 | >>> records.deleted_identifiers[:2] 96 | ['BYVANCK:BYVANCK:5562', 'BYVANCK:BYVANCK:5567'] 97 | >>> records.identifiers 98 | ['BYVANCK:3477'] 99 | >>> record = oai.get(records.identifiers[0]) 100 | http://services.kb.nl/mdo/oai?verb=GetRecord&identifier=ByvanckB:ByvanckB:3477&metadataPrefix=dcx 101 | >>> record.alto 102 | False 103 | >>> image = record.image 104 | http://imageviewer.kb.nl/ImagingService/imagingService?id=BYVANCKB:mimi_78d38:dl1_183r_min 105 | >>> len(image) 106 | 109800 107 | """ 108 | 109 | if __name__ == "__main__": 110 | import doctest 111 | doctest.testmod() 112 | -------------------------------------------------------------------------------- /test/sru_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import sys 6 | 7 | sys.path.append('..' + os.sep) 8 | sys.path.append('.' + os.sep) 9 | 10 | def oai_anp(): 11 | """ 12 | >>> from kb.nl.api import sru 13 | >>> from kb.nl.helpers import alto_to_text 14 | >>> sru.DEBUG = True 15 | >>> response = sru.search("beatrix AND juliana AND Bernhard AND gelukwensen", "ANP") 16 | run_query: https://jsru.kb.nl/sru/sru?version=1.2&maximumRecords=1&operation=searchRetrieve&startRecord=1&recordSchema=dcx&x-collection=ANP&query=beatrix%20AND%20juliana%20AND%20Bernhard%20AND%20gelukwensen 17 | >>> for record in response.records: 18 | ... print(record.dates) 19 | run_query: https://jsru.kb.nl/sru/sru?version=1.2&maximumRecords=1&operation=searchRetrieve&startRecord=1&recordSchema=dcx&x-collection=ANP&query=beatrix%20AND%20juliana%20AND%20Bernhard%20AND%20gelukwensen 20 | ['1968/09/25 00:00:00'] 21 | run_query: https://jsru.kb.nl/sru/sru?version=1.2&maximumRecords=1&operation=searchRetrieve&startRecord=2&recordSchema=dcx&x-collection=ANP&query=beatrix%20AND%20juliana%20AND%20Bernhard%20AND%20gelukwensen 22 | ['1967/04/28 00:00:00'] 23 | run_query: https://jsru.kb.nl/sru/sru?version=1.2&maximumRecords=1&operation=searchRetrieve&startRecord=3&recordSchema=dcx&x-collection=ANP&query=beatrix%20AND%20juliana%20AND%20Bernhard%20AND%20gelukwensen 24 | ['1966/02/21 00:00:00'] 25 | run_query: https://jsru.kb.nl/sru/sru?version=1.2&maximumRecords=1&operation=searchRetrieve&startRecord=4&recordSchema=dcx&x-collection=ANP&query=beatrix%20AND%20juliana%20AND%20Bernhard%20AND%20gelukwensen 26 | ['1966/03/10 00:00:00'] 27 | run_query: https://jsru.kb.nl/sru/sru?version=1.2&maximumRecords=1&operation=searchRetrieve&startRecord=5&recordSchema=dcx&x-collection=ANP&query=beatrix%20AND%20juliana%20AND%20Bernhard%20AND%20gelukwensen 28 | ['1966/03/09 00:00:00'] 29 | run_query: https://jsru.kb.nl/sru/sru?version=1.2&maximumRecords=1&operation=searchRetrieve&startRecord=6&recordSchema=dcx&x-collection=ANP&query=beatrix%20AND%20juliana%20AND%20Bernhard%20AND%20gelukwensen 30 | ['1969/10/11 00:00:00'] 31 | run_query: https://jsru.kb.nl/sru/sru?version=1.2&maximumRecords=1&operation=searchRetrieve&startRecord=7&recordSchema=dcx&x-collection=ANP&query=beatrix%20AND%20juliana%20AND%20Bernhard%20AND%20gelukwensen 32 | ['1965/06/29 00:00:00'] 33 | >>> sru.DEBUG = False 34 | >>> response = sru.search('"J.A. Deelder" AND "gevallen"', "GGC") 35 | >>> for i, record in enumerate(sorted(response.records)): 36 | ... print(record.titles, record.dates) 37 | ... break 38 | ['Zij die vielen zijn gevallen'] ['[ca. 1990]'] 39 | """ 40 | 41 | 42 | if __name__ == "__main__": 43 | import doctest 44 | doctest.testmod() 45 | -------------------------------------------------------------------------------- /test/test_data/anp_1937-10-01_1_alto.xml: -------------------------------------------------------------------------------- 1 | 2 | pixel 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1132 | 1133 | 1134 | 1135 | 1136 | 1137 | 1138 | 1139 | 1140 | 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | 1155 | 1156 | 1157 | 1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | 1166 | 1167 | 1168 | 1169 | 1170 | 1171 | 1172 | 1173 | 1174 | 1175 | 1176 | 1177 | 1178 | 1179 | 1180 | 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | 1187 | 1188 | 1189 | 1190 | 1191 | 1192 | 1193 | 1194 | 1195 | 1196 | 1197 | 1198 | 1199 | 1200 | 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | 1217 | 1218 | 1219 | 1220 | 1221 | 1222 | 1223 | 1224 | 1225 | 1226 | 1227 | 1228 | 1229 | 1230 | 1231 | 1232 | 1233 | 1234 | 1235 | 1236 | 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | 1251 | 1252 | 1253 | 1254 | 1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 1271 | 1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | 1282 | 1283 | 1284 | 1285 | 1286 | 1287 | 1288 | 1289 | 1290 | 1291 | 1292 | 1293 | 1294 | 1295 | 1296 | 1297 | 1298 | 1299 | 1300 | 1301 | 1302 | --------------------------------------------------------------------------------