From d9f4136fa41cd0e6d1e357f3bed2ba82eb3dab17 Mon Sep 17 00:00:00 2001 From: alphardex <2582347430@qq.com> Date: Wed, 16 Jan 2019 22:45:05 +0800 Subject: [PATCH] init project --- .coveragerc | 2 + .flaskenv | 3 + .gitignore | 17 ++ Pipfile | 26 ++ Pipfile.lock | 456 +++++++++++++++++++++++++++++ Procfile | 1 + README.md | 53 ++++ app.json | 9 + rsshub/__init__.py | 61 ++++ rsshub/blueprints/__init__.py | 0 rsshub/blueprints/main.py | 46 +++ rsshub/config.py | 32 ++ rsshub/extensions.py | 8 + rsshub/spiders/__init__.py | 0 rsshub/spiders/guokr/scientific.py | 23 ++ rsshub/spiders/toutiao/today.py | 22 ++ rsshub/static/css/style.css | 48 +++ rsshub/static/favicon.ico | Bin 0 -> 7524 bytes rsshub/templates/errors/400.html | 9 + rsshub/templates/errors/404.html | 9 + rsshub/templates/errors/500.html | 9 + rsshub/templates/layout.html | 69 +++++ rsshub/templates/main/atom.xml | 22 ++ rsshub/templates/main/feeds.html | 11 + rsshub/templates/main/index.html | 13 + rsshub/utils.py | 25 ++ tests/__init__.py | 0 tests/base.py | 15 + tests/test_cli.py | 7 + tests/test_errors.py | 30 ++ tests/test_main.py | 8 + wsgi.py | 10 + 32 files changed, 1044 insertions(+) create mode 100644 .coveragerc create mode 100644 .flaskenv create mode 100644 .gitignore create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100644 Procfile create mode 100644 README.md create mode 100644 app.json create mode 100644 rsshub/__init__.py create mode 100644 rsshub/blueprints/__init__.py create mode 100644 rsshub/blueprints/main.py create mode 100644 rsshub/config.py create mode 100644 rsshub/extensions.py create mode 100644 rsshub/spiders/__init__.py create mode 100644 rsshub/spiders/guokr/scientific.py create mode 100644 rsshub/spiders/toutiao/today.py create mode 100644 rsshub/static/css/style.css create mode 100644 rsshub/static/favicon.ico create mode 100644 rsshub/templates/errors/400.html create mode 100644 rsshub/templates/errors/404.html create mode 100644 rsshub/templates/errors/500.html create mode 100644 rsshub/templates/layout.html create mode 100644 rsshub/templates/main/atom.xml create mode 100644 rsshub/templates/main/feeds.html create mode 100644 rsshub/templates/main/index.html create mode 100644 rsshub/utils.py create mode 100644 tests/__init__.py create mode 100644 tests/base.py create mode 100644 tests/test_cli.py create mode 100644 tests/test_errors.py create mode 100644 tests/test_main.py create mode 100644 wsgi.py diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..f32ec8f --- /dev/null +++ b/.coveragerc @@ -0,0 +1,2 @@ +[run] +source = rsshub \ No newline at end of file diff --git a/.flaskenv b/.flaskenv new file mode 100644 index 0000000..c40645d --- /dev/null +++ b/.flaskenv @@ -0,0 +1,3 @@ +FLASK_ENV=development +FLASK_APP=rsshub +FLASK_DEBUG=1 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..60bba3f --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +.pytest_cache + +# Distribution / packaging +build/ +dist/ +*.egg-info/ +.idea +venv + +# Others +.vscode +.coverage +htmlcov/ +data-dev.db \ No newline at end of file diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..612bf87 --- /dev/null +++ b/Pipfile @@ -0,0 +1,26 @@ +[[source]] +url = "https://pypi.doubanio.com/simple" +verify_ssl = true +name = "pypi" + +[packages] +bootstrap-flask = "*" +flask-debugtoolbar = "*" +flask-moment = "*" +Flask = "*" +python-dotenv = "*" +"psycopg2" = "*" +gunicorn = "*" +requests = "*" +parsel = "*" + +[dev-packages] +coverage = "*" +pdir2 = "*" +ptpython = "*" +yapf = "*" +pylint = "*" + +[scripts] +test = "python -m unittest discover" +coverage = "coverage run -m unittest discover" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..58e3a40 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,456 @@ +{ + "_meta": { + "hash": { + "sha256": "1a952bf63349734b6daa3bc57851614425ebe4a867ca1826881a6b0324324c27" + }, + "pipfile-spec": 6, + "requires": {}, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.doubanio.com/simple", + "verify_ssl": true + } + ] + }, + "default": { + "blinker": { + "hashes": [ + "sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6" + ], + "version": "==1.4" + }, + "bootstrap-flask": { + "hashes": [ + "sha256:1f7462261b8104687807ce74b397270e7ade07c491ad7d53f215940d8433d756", + "sha256:ce5cf19c46b8d385923dc2f9ca76b92fc08c1a8d7dcb5d177325a85a94d71045" + ], + "index": "pypi", + "version": "==1.0.9" + }, + "certifi": { + "hashes": [ + "sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7", + "sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033" + ], + "version": "==2018.11.29" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "click": { + "hashes": [ + "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", + "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" + ], + "version": "==7.0" + }, + "cssselect": { + "hashes": [ + "sha256:066d8bc5229af09617e24b3ca4d52f1f9092d9e061931f4184cd572885c23204", + "sha256:3b5103e8789da9e936a68d993b70df732d06b8bb9a337a05ed4eb52c17ef7206" + ], + "version": "==1.0.3" + }, + "flask": { + "hashes": [ + "sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48", + "sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05" + ], + "index": "pypi", + "version": "==1.0.2" + }, + "flask-debugtoolbar": { + "hashes": [ + "sha256:3d9657bc0c3633ace429e3ff451742bb59d1b7a7b95c9eb23a65ac9be2812959", + "sha256:ec810083123aae0632eb32ba11e1cb4cdace81e7ce6c5009dd06c5204afbce52" + ], + "index": "pypi", + "version": "==0.10.1" + }, + "flask-moment": { + "hashes": [ + "sha256:71a601fcd5be4742227251641cb706c109680b54c5fb25c5d2ed96e576ec3b4d", + "sha256:af7ccd599d85e751ff1f7661904daa51df9950e9bc9bd4ccf174bd38ccbc401f" + ], + "index": "pypi", + "version": "==0.6.0" + }, + "gunicorn": { + "hashes": [ + "sha256:aa8e0b40b4157b36a5df5e599f45c9c76d6af43845ba3b3b0efe2c70473c2471", + "sha256:fa2662097c66f920f53f70621c6c58ca4a3c4d3434205e608e121b5b3b71f4f3" + ], + "index": "pypi", + "version": "==19.9.0" + }, + "idna": { + "hashes": [ + "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", + "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" + ], + "version": "==2.8" + }, + "itsdangerous": { + "hashes": [ + "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", + "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749" + ], + "version": "==1.1.0" + }, + "jinja2": { + "hashes": [ + "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", + "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4" + ], + "version": "==2.10" + }, + "lxml": { + "hashes": [ + "sha256:0dd6589fa75d369ba06d2b5f38dae107f76ea127f212f6a7bee134f6df2d1d21", + "sha256:1afbac344aa68c29e81ab56c1a9411c3663157b5aee5065b7fa030b398d4f7e0", + "sha256:1baad9d073692421ad5dbbd81430aba6c7f5fdc347f03537ae046ddf2c9b2297", + "sha256:1d8736421a2358becd3edf20260e41a06a0bf08a560480d3a5734a6bcbacf591", + "sha256:1e1d9bddc5afaddf0de76246d3f2152f961697ad7439c559f179002682c45801", + "sha256:1f179dc8b2643715f020f4d119d5529b02cd794c1c8f305868b73b8674d2a03f", + "sha256:241fb7bdf97cb1df1edfa8f0bcdfd80525d4023dac4523a241907c8b2f44e541", + "sha256:2f9765ee5acd3dbdcdc0d0c79309e01f7c16bc8d39b49250bf88de7b46daaf58", + "sha256:312e1e1b1c3ce0c67e0b8105317323e12807955e8186872affb667dbd67971f6", + "sha256:3273db1a8055ca70257fd3691c6d2c216544e1a70b673543e15cc077d8e9c730", + "sha256:34dfaa8c02891f9a246b17a732ca3e99c5e42802416628e740a5d1cb2f50ff49", + "sha256:3aa3f5288af349a0f3a96448ebf2e57e17332d99f4f30b02093b7948bd9f94cc", + "sha256:51102e160b9d83c1cc435162d90b8e3c8c93b28d18d87b60c56522d332d26879", + "sha256:56115fc2e2a4140e8994eb9585119a1ae9223b506826089a3ba753a62bd194a6", + "sha256:69d83de14dbe8fe51dccfd36f88bf0b40f5debeac763edf9f8325180190eba6e", + "sha256:99fdce94aeaa3ccbdfcb1e23b34273605c5853aa92ec23d84c84765178662c6c", + "sha256:a7c0cd5b8a20f3093ee4a67374ccb3b8a126743b15a4d759e2a1bf098faac2b2", + "sha256:abe12886554634ed95416a46701a917784cb2b4c77bfacac6916681d49bbf83d", + "sha256:b4f67b5183bd5f9bafaeb76ad119e977ba570d2b0e61202f534ac9b5c33b4485", + "sha256:bdd7c1658475cc1b867b36d5c4ed4bc316be8d3368abe03d348ba906a1f83b0e", + "sha256:c6f24149a19f611a415a51b9bc5f17b6c2f698e0d6b41ffb3fa9f24d35d05d73", + "sha256:d1e111b3ab98613115a208c1017f266478b0ab224a67bc8eac670fa0bad7d488", + "sha256:d6520aa965773bbab6cb7a791d5895b00d02cf9adc93ac2bf4edb9ac1a6addc5", + "sha256:dd185cde2ccad7b649593b0cda72021bc8a91667417001dbaf24cd746ecb7c11", + "sha256:de2e5b0828a9d285f909b5d2e9d43f1cf6cf21fe65bc7660bdaa1780c7b58298", + "sha256:f726444b8e909c4f41b4fde416e1071cf28fa84634bfb4befdf400933b6463af" + ], + "version": "==4.3.0" + }, + "markupsafe": { + "hashes": [ + "sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432", + "sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b", + "sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9", + "sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af", + "sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834", + "sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd", + "sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d", + "sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7", + "sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b", + "sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3", + "sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c", + "sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2", + "sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7", + "sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36", + "sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1", + "sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e", + "sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1", + "sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c", + "sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856", + "sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550", + "sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492", + "sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672", + "sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401", + "sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6", + "sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6", + "sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c", + "sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd", + "sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1" + ], + "version": "==1.1.0" + }, + "parsel": { + "hashes": [ + "sha256:493a9214acbdcb4487a084d95344c25e85e90426a67311ea0425dc5df8dc24b9", + "sha256:9ccd82b8a122345601f6f9209e972c0e8c3518a188fcff2d37cb4d7bc570b4b8" + ], + "index": "pypi", + "version": "==1.5.1" + }, + "psycopg2": { + "hashes": [ + "sha256:10e391687b171878181e71736d0effe3772314a339d9ae30995ec8171a0c834e", + "sha256:1283f9d45e458c2dcb15ba89367923563f90ef636fe78ee22df75183484a0237", + "sha256:1a9c32e4d140bea225f9821d993b2e53c913e717ea97b851246aa9b300095d8f", + "sha256:1be6f2438d2b71fec7b07c3c0949dd321b04349c382907ea76b36120edec8300", + "sha256:20ca6f29e118b8dd7133e8708b3fba2881e70a4e0841f874ed23985b7201a076", + "sha256:227c115b3c1f65d61385e51ac690b91b584640aefb45bffacd4bd33d02ed7221", + "sha256:27959abe64ca1fc6d8cd11a71a1f421d8287831a3262bd4cacd43bbf43cc3c82", + "sha256:2b2daf1fe30a58300542aea679fd87d1e1c2afd36e7644837b7954fa2dbacb92", + "sha256:36e51a51f295fdf67bcf05e7b1877011a6b39e6622b0013fe31c5025241873a3", + "sha256:3992b9b914f2eb77dc07e8045d2ca979e491612808bc5c7cd68f307469acf9f6", + "sha256:39a11de2335ad45ececed43ab851d36a4c52843d756471b940804f301792781e", + "sha256:3c2afe9ef0d1649005e3ccf93c1aaccd6f8ee379530e763d3b3b77f406b7c0ae", + "sha256:3fb18e0e52807fe3a300dc1b5421aa492d5e759550918f597d61863419482535", + "sha256:55eab94de96ee9702f23283e9c8b03cfdb0001e2b14d5d2e1bd5ff8114b96b9f", + "sha256:7e95c0ab7e7e6e452586f35d4d8966b1e924c8dd2c23977e3ea4968770ff1d26", + "sha256:7f47514dbddf604f196fcfe5da955537f04691bef8124aff5632316a78d992b7", + "sha256:8345370356bb4bddf93acbcfd0357163dd6b09471937adcfb38a2fbb49bdce53", + "sha256:8bc6ecb220c0b88d3742042013129c817c44459795c97e9ce1bca70a3f37a53b", + "sha256:8df623f248be15d1725faf5f333791678775047f12f17a90d29b5d22573f5cdc", + "sha256:9645f1305e4268cc0fc88c823cd6c91de27c003e183c233a6a230e5e963039ee", + "sha256:a68719ed5be8373dd72c9e45d55f7a202285e05a2e392eaa8872a67ea47d7d20", + "sha256:aca0edf062ec09e954fdf0cc93d3a872362701210983a1442549e703aedec25d", + "sha256:b0dd2114d93d8f424bb8ae76e0dc540f104b70ca9163172c05e7700b1459d4c9", + "sha256:b2c09359d6802279efb9efb3f91a9c94567151baee95175f9b637ea628f35244", + "sha256:ca7bc37b1efb7cc25271bf10f398462ed975d95259af1406d38fcb268466e34f", + "sha256:e64235d9013ebf6319cb9654e08f5066112c34d8c4cc41186254ab9c3d6d5b9b", + "sha256:ec9be679c0065667503851141c31fa699e1cc69ded3ba8e5d3673dd5a6eb1370", + "sha256:eca00d0f91fcb44d88b12f1fd16ad138e38fa07debb79587e2b7ff1fe80d72b9", + "sha256:f256e807b8b2b45b6af60d7f2bb5194aab2f4acc861241c4d8ef942a55f5030d", + "sha256:fce7612a3bd6a7ba95799f88285653bf130bd7ca066b52674d5f850108b2aec0" + ], + "index": "pypi", + "version": "==2.7.6.1" + }, + "python-dotenv": { + "hashes": [ + "sha256:a84569d0e00d178bc5b957f7ff208bf49287cbf61857c31c258c4a91f571527b", + "sha256:c9b1ddd3cdbe75c7d462cb84674d87130f4b948f090f02c7d7144779afb99ae0" + ], + "index": "pypi", + "version": "==0.10.1" + }, + "requests": { + "hashes": [ + "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", + "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b" + ], + "index": "pypi", + "version": "==2.21.0" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + }, + "urllib3": { + "hashes": [ + "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", + "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22" + ], + "version": "==1.24.1" + }, + "w3lib": { + "hashes": [ + "sha256:67eacd24f4f233a80bc472835ad6aad99616e49aa1d795f3dbb7b8b028fec6cf", + "sha256:e54db912ae1c79d3e246b16d3645bdf701f70ac4f8a2fc46eeb284cc02a31cd7" + ], + "version": "==1.20.0" + }, + "werkzeug": { + "hashes": [ + "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c", + "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b" + ], + "version": "==0.14.1" + } + }, + "develop": { + "astroid": { + "hashes": [ + "sha256:35b032003d6a863f5dcd7ec11abd5cd5893428beaa31ab164982403bcb311f22", + "sha256:6a5d668d7dc69110de01cdf7aeec69a679ef486862a0850cc0fd5571505b6b7e" + ], + "version": "==2.1.0" + }, + "colorama": { + "hashes": [ + "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", + "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48" + ], + "markers": "platform_system == 'Windows'", + "version": "==0.4.1" + }, + "coverage": { + "hashes": [ + "sha256:09e47c529ff77bf042ecfe858fb55c3e3eb97aac2c87f0349ab5a7efd6b3939f", + "sha256:0a1f9b0eb3aa15c990c328535655847b3420231af299386cfe5efc98f9c250fe", + "sha256:0cc941b37b8c2ececfed341444a456912e740ecf515d560de58b9a76562d966d", + "sha256:10e8af18d1315de936d67775d3a814cc81d0747a1a0312d84e27ae5610e313b0", + "sha256:1b4276550b86caa60606bd3572b52769860a81a70754a54acc8ba789ce74d607", + "sha256:1e8a2627c48266c7b813975335cfdea58c706fe36f607c97d9392e61502dc79d", + "sha256:2b224052bfd801beb7478b03e8a66f3f25ea56ea488922e98903914ac9ac930b", + "sha256:447c450a093766744ab53bf1e7063ec82866f27bcb4f4c907da25ad293bba7e3", + "sha256:46101fc20c6f6568561cdd15a54018bb42980954b79aa46da8ae6f008066a30e", + "sha256:4710dc676bb4b779c4361b54eb308bc84d64a2fa3d78e5f7228921eccce5d815", + "sha256:510986f9a280cd05189b42eee2b69fecdf5bf9651d4cd315ea21d24a964a3c36", + "sha256:5535dda5739257effef56e49a1c51c71f1d37a6e5607bb25a5eee507c59580d1", + "sha256:5a7524042014642b39b1fcae85fb37556c200e64ec90824ae9ecf7b667ccfc14", + "sha256:5f55028169ef85e1fa8e4b8b1b91c0b3b0fa3297c4fb22990d46ff01d22c2d6c", + "sha256:6694d5573e7790a0e8d3d177d7a416ca5f5c150742ee703f3c18df76260de794", + "sha256:6831e1ac20ac52634da606b658b0b2712d26984999c9d93f0c6e59fe62ca741b", + "sha256:77f0d9fa5e10d03aa4528436e33423bfa3718b86c646615f04616294c935f840", + "sha256:828ad813c7cdc2e71dcf141912c685bfe4b548c0e6d9540db6418b807c345ddd", + "sha256:85a06c61598b14b015d4df233d249cd5abfa61084ef5b9f64a48e997fd829a82", + "sha256:8cb4febad0f0b26c6f62e1628f2053954ad2c555d67660f28dfb1b0496711952", + "sha256:a5c58664b23b248b16b96253880b2868fb34358911400a7ba39d7f6399935389", + "sha256:aaa0f296e503cda4bc07566f592cd7a28779d433f3a23c48082af425d6d5a78f", + "sha256:ab235d9fe64833f12d1334d29b558aacedfbca2356dfb9691f2d0d38a8a7bfb4", + "sha256:b3b0c8f660fae65eac74fbf003f3103769b90012ae7a460863010539bb7a80da", + "sha256:bab8e6d510d2ea0f1d14f12642e3f35cefa47a9b2e4c7cea1852b52bc9c49647", + "sha256:c45297bbdbc8bb79b02cf41417d63352b70bcb76f1bbb1ee7d47b3e89e42f95d", + "sha256:d19bca47c8a01b92640c614a9147b081a1974f69168ecd494687c827109e8f42", + "sha256:d64b4340a0c488a9e79b66ec9f9d77d02b99b772c8b8afd46c1294c1d39ca478", + "sha256:da969da069a82bbb5300b59161d8d7c8d423bc4ccd3b410a9b4d8932aeefc14b", + "sha256:ed02c7539705696ecb7dc9d476d861f3904a8d2b7e894bd418994920935d36bb", + "sha256:ee5b8abc35b549012e03a7b1e86c09491457dba6c94112a2482b18589cc2bdb9" + ], + "index": "pypi", + "version": "==4.5.2" + }, + "docopt": { + "hashes": [ + "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491" + ], + "version": "==0.6.2" + }, + "isort": { + "hashes": [ + "sha256:1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af", + "sha256:b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8", + "sha256:ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497" + ], + "version": "==4.3.4" + }, + "jedi": { + "hashes": [ + "sha256:571702b5bd167911fe9036e5039ba67f820d6502832285cde8c881ab2b2149fd", + "sha256:c8481b5e59d34a5c7c42e98f6625e633f6ef59353abea6437472c7ec2093f191" + ], + "version": "==0.13.2" + }, + "lazy-object-proxy": { + "hashes": [ + "sha256:0ce34342b419bd8f018e6666bfef729aec3edf62345a53b537a4dcc115746a33", + "sha256:1b668120716eb7ee21d8a38815e5eb3bb8211117d9a90b0f8e21722c0758cc39", + "sha256:209615b0fe4624d79e50220ce3310ca1a9445fd8e6d3572a896e7f9146bbf019", + "sha256:27bf62cb2b1a2068d443ff7097ee33393f8483b570b475db8ebf7e1cba64f088", + "sha256:27ea6fd1c02dcc78172a82fc37fcc0992a94e4cecf53cb6d73f11749825bd98b", + "sha256:2c1b21b44ac9beb0fc848d3993924147ba45c4ebc24be19825e57aabbe74a99e", + "sha256:2df72ab12046a3496a92476020a1a0abf78b2a7db9ff4dc2036b8dd980203ae6", + "sha256:320ffd3de9699d3892048baee45ebfbbf9388a7d65d832d7e580243ade426d2b", + "sha256:50e3b9a464d5d08cc5227413db0d1c4707b6172e4d4d915c1c70e4de0bbff1f5", + "sha256:5276db7ff62bb7b52f77f1f51ed58850e315154249aceb42e7f4c611f0f847ff", + "sha256:61a6cf00dcb1a7f0c773ed4acc509cb636af2d6337a08f362413c76b2b47a8dd", + "sha256:6ae6c4cb59f199d8827c5a07546b2ab7e85d262acaccaacd49b62f53f7c456f7", + "sha256:7661d401d60d8bf15bb5da39e4dd72f5d764c5aff5a86ef52a042506e3e970ff", + "sha256:7bd527f36a605c914efca5d3d014170b2cb184723e423d26b1fb2fd9108e264d", + "sha256:7cb54db3535c8686ea12e9535eb087d32421184eacc6939ef15ef50f83a5e7e2", + "sha256:7f3a2d740291f7f2c111d86a1c4851b70fb000a6c8883a59660d95ad57b9df35", + "sha256:81304b7d8e9c824d058087dcb89144842c8e0dea6d281c031f59f0acf66963d4", + "sha256:933947e8b4fbe617a51528b09851685138b49d511af0b6c0da2539115d6d4514", + "sha256:94223d7f060301b3a8c09c9b3bc3294b56b2188e7d8179c762a1cda72c979252", + "sha256:ab3ca49afcb47058393b0122428358d2fbe0408cf99f1b58b295cfeb4ed39109", + "sha256:bd6292f565ca46dee4e737ebcc20742e3b5be2b01556dafe169f6c65d088875f", + "sha256:cb924aa3e4a3fb644d0c463cad5bc2572649a6a3f68a7f8e4fbe44aaa6d77e4c", + "sha256:d0fc7a286feac9077ec52a927fc9fe8fe2fabab95426722be4c953c9a8bede92", + "sha256:ddc34786490a6e4ec0a855d401034cbd1242ef186c20d79d2166d6a4bd449577", + "sha256:e34b155e36fa9da7e1b7c738ed7767fc9491a62ec6af70fe9da4a057759edc2d", + "sha256:e5b9e8f6bda48460b7b143c3821b21b452cb3a835e6bbd5dd33aa0c8d3f5137d", + "sha256:e81ebf6c5ee9684be8f2c87563880f93eedd56dd2b6146d8a725b50b7e5adb0f", + "sha256:eb91be369f945f10d3a49f5f9be8b3d0b93a4c2be8f8a5b83b0571b8123e0a7a", + "sha256:f460d1ceb0e4a5dcb2a652db0904224f367c9b3c1470d5a7683c0480e582468b" + ], + "version": "==1.3.1" + }, + "mccabe": { + "hashes": [ + "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", + "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" + ], + "version": "==0.6.1" + }, + "parso": { + "hashes": [ + "sha256:35704a43a3c113cce4de228ddb39aab374b8004f4f2407d070b6a2ca784ce8a2", + "sha256:895c63e93b94ac1e1690f5fdd40b65f07c8171e3e53cbd7793b5b96c0e0a7f24" + ], + "version": "==0.3.1" + }, + "pdir2": { + "hashes": [ + "sha256:71761d0fe879de2c91fc36ecdb9a7f20d059deee7d94d0f10c2dea2cca1c1929", + "sha256:f3c08df2387e6d34f2ce22be78de9dd809fcffae96a69be733a4e99a2b5d8872" + ], + "index": "pypi", + "version": "==0.3.1.post2" + }, + "prompt-toolkit": { + "hashes": [ + "sha256:c1d6aff5252ab2ef391c2fe498ed8c088066f66bc64a8d5c095bbf795d9fec34", + "sha256:d4c47f79b635a0e70b84fdb97ebd9a274203706b1ee5ed44c10da62755cf3ec9", + "sha256:fd17048d8335c1e6d5ee403c3569953ba3eb8555d710bfc548faf0712666ea39" + ], + "version": "==2.0.7" + }, + "ptpython": { + "hashes": [ + "sha256:51a74abe931f692360a32d650c2ba1ca329c08f3ed9b1de8abcd1164e0b0a6a7", + "sha256:938ee050e37d61c138dbbeb21383dfef8b9ed4ffb453a5f34041f42025bf5042", + "sha256:ebe9d68ea7532ec8ab306d4bdc7ec393701cd9bbd6eff0aa3067c821f99264d4" + ], + "index": "pypi", + "version": "==2.0.4" + }, + "pygments": { + "hashes": [ + "sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a", + "sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d" + ], + "version": "==2.3.1" + }, + "pylint": { + "hashes": [ + "sha256:689de29ae747642ab230c6d37be2b969bf75663176658851f456619aacf27492", + "sha256:771467c434d0d9f081741fec1d64dfb011ed26e65e12a28fe06ca2f61c4d556c" + ], + "index": "pypi", + "version": "==2.2.2" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + }, + "wcwidth": { + "hashes": [ + "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", + "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c" + ], + "version": "==0.1.7" + }, + "wrapt": { + "hashes": [ + "sha256:e03f19f64d81d0a3099518ca26b04550026f131eced2e76ced7b85c6b8d32128" + ], + "version": "==1.11.0" + }, + "yapf": { + "hashes": [ + "sha256:8aa7f9abdb97b4da4d3227306b88477982daafef0a96cc41639754ca31f46d55", + "sha256:f2df5891481f94ddadfbf8ae8ae499080752cfb06005a31bbb102f3012f8b944" + ], + "index": "pypi", + "version": "==0.25.0" + } + } +} diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..efae4d8 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: flask db upgrade; gunicorn wsgi:app \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7d6824c --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +# RSSHub + +> 🍰 万物皆可 RSS + +RSSHub 是一个轻量、易于扩展的 RSS 生成器,可以给任何奇奇怪怪的内容生成 RSS 订阅源 + +本项目是[原RSSHub](https://github.com/DIYgod/RSSHub)的Python实现。 + +*其实用Python写爬虫要比JS更方便:p* + +DEMO地址:https://rsshub-python.herokuapp.com + +## RSS过滤 + +你可以通过以下查询字符串来过滤RSS的内容: + +- include_title: 搜索标题 +- include_description: 搜索描述 +- exclude_title: 排除标题 +- exclude_description: 排除描述 +- limit: 限制条数 + +## 贡献RSS方法 + +1. fork这份仓库 +2. 在spiders文件夹下创建新的爬虫目录和脚本,编写爬虫,参考我的[爬虫教程](https://alphardex.github.io/2018/12/15/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB%E7%B2%BE%E8%A6%81/) +3. 在blueprints的main.py中添加对应的路由(按照之前路由的格式) +4. 提pr + +## 部署 + +### 搭建 + +首先确保安装了[pipenv](https://github.com/pypa/pipenv) + +``` bash +git clone https://github.com/alphardex/rsshub +cd rsshub +pipenv install --dev +pipenv shell +``` + +### 运行 + +``` bash +flask run +``` + +### 部署到Heroku + +[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/alphardex/rsshub) + +记得在环境变量中把FLASK_CONFIG设为production diff --git a/app.json b/app.json new file mode 100644 index 0000000..edb9eba --- /dev/null +++ b/app.json @@ -0,0 +1,9 @@ +{ + "name": "RSSHub", + "description": "A rsshub powered by flask.", + "repository": "https://github.com/alphardex/rsshub", + "keywords": [ + "python", + "flask" + ] +} \ No newline at end of file diff --git a/rsshub/__init__.py b/rsshub/__init__.py new file mode 100644 index 0000000..33e0dc9 --- /dev/null +++ b/rsshub/__init__.py @@ -0,0 +1,61 @@ +import os +import click +from flask import Flask, render_template +from flask.cli import with_appcontext +from rsshub.config import config +from rsshub.extensions import * +from rsshub.blueprints.main import bp as main_bp +from rsshub.utils import XMLResponse + + +def create_app(config_name=None): + if config_name is None: + config_name = os.getenv('FLASK_CONFIG', 'development') + + app = Flask(__name__) + app.config.from_object(config[config_name]) + app.response_class = XMLResponse + + register_blueprints(app) + register_extensions(app) + register_errors(app) + register_cli(app) + + return app + + +def register_extensions(app): + bootstrap.init_app(app) + debugtoolbar.init_app(app) + moment.init_app(app) + + +def register_blueprints(app): + app.register_blueprint(main_bp) + + +def register_errors(app): + @app.errorhandler(400) + def bad_request(e): + return render_template('errors/400.html'), 400 + + @app.errorhandler(404) + def page_not_found(e): + return render_template('errors/404.html'), 404 + + @app.errorhandler(500) + def internal_server_error(e): + return render_template('errors/500.html'), 500 + + +def register_cli(app): + @app.cli.command() + @with_appcontext + def ptshell(): + """Use ptpython as shell.""" + try: + from ptpython.repl import embed + if not app.config['TESTING']: + embed(app.make_shell_context()) + except ImportError: + click.echo('ptpython not installed! Use the default shell instead.') diff --git a/rsshub/blueprints/__init__.py b/rsshub/blueprints/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rsshub/blueprints/main.py b/rsshub/blueprints/main.py new file mode 100644 index 0000000..de27650 --- /dev/null +++ b/rsshub/blueprints/main.py @@ -0,0 +1,46 @@ +from flask import Blueprint, render_template, current_app, request + + +bp = Blueprint('main', __name__) + + +@bp.route('/') +def index(): + return render_template('main/index.html') + + +@bp.route('/feeds') +def feeds(): + feed_rules = [rule for rule in current_app.url_map._rules if 'main' in rule.endpoint][:-2] + return render_template('main/feeds.html', rules=feed_rules) + + +@bp.app_template_global() +def filter_content(ctx): + include_title = request.args.get('include_title') + include_description = request.args.get('include_description') + exclude_title = request.args.get('exclude_title') + exclude_description = request.args.get('exclude_description') + limit = request.args.get('limit', type=int) + items = ctx['items'].copy() + items = [item for item in items if include_title in item['title']] if include_title else items + items = [item for item in items if include_description in item['description']] if include_description else items + items = [item for item in items if exclude_title not in item['title']] if exclude_title else items + items = [item for item in items if exclude_description not in item['description']] if exclude_description else items + items = items[:limit] if limit else items + ctx = ctx.copy() + ctx['items'] = items + return ctx + + +#---------- feed路由从这里开始 -----------# +@bp.route('/guokr/scentific') +def guokr_scientific(): + from rsshub.spiders.guokr.scientific import ctx + return render_template('main/atom.xml', **filter_content(ctx)) + + +@bp.route('/toutiao/today') +def toutiao_today(): + from rsshub.spiders.toutiao.today import ctx + return render_template('main/atom.xml', **filter_content(ctx)) diff --git a/rsshub/config.py b/rsshub/config.py new file mode 100644 index 0000000..da9b0bb --- /dev/null +++ b/rsshub/config.py @@ -0,0 +1,32 @@ +import os +import sys + + +basedir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + + +class BaseConfig: + SITE_NAME = 'RSSHub' + GITHUB_USERNAME = 'alphardex' + EMAIL = '2582347430@qq.com' + SECRET_KEY = os.environ.get('SECRET_KEY') or 'f43hrt53et53' + DEBUG_TB_INTERCEPT_REDIRECTS = False + + +class DevelopmentConfig(BaseConfig): + pass + + +class TestingConfig(BaseConfig): + TESTING = True + + +class ProductionConfig(BaseConfig): + pass + + +config = { + 'development': DevelopmentConfig, + 'testing': TestingConfig, + 'production': ProductionConfig +} diff --git a/rsshub/extensions.py b/rsshub/extensions.py new file mode 100644 index 0000000..3d5cc33 --- /dev/null +++ b/rsshub/extensions.py @@ -0,0 +1,8 @@ +from flask_bootstrap import Bootstrap +from flask_debugtoolbar import DebugToolbarExtension +from flask_moment import Moment + + +bootstrap = Bootstrap() +debugtoolbar = DebugToolbarExtension() +moment = Moment() diff --git a/rsshub/spiders/__init__.py b/rsshub/spiders/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rsshub/spiders/guokr/scientific.py b/rsshub/spiders/guokr/scientific.py new file mode 100644 index 0000000..4911352 --- /dev/null +++ b/rsshub/spiders/guokr/scientific.py @@ -0,0 +1,23 @@ +import requests + +data = requests.get( + 'https://www.guokr.com/apis/minisite/article.json?retrieve_type=by_subject' +).json()['result'] + + +def parse(d): + item = {} + item['title'] = d['title'] + item['description'] = f"{d['summary']}
Qn|V!?t_tUz(6NGYzt-Q9~9hXTdjrNs*rw-$%s?)Lb- z_b=!R6S>+_* zAKeTNve1*%r;|fEz3tv$1gwF`aHIJpQE<0Oy4}P6eEyK1^+LZg>`k~FBjw~@Ilgq> zbft_`X96p+JO=g^utP*@3BS}jkpBfXQ<#G}lWEbvzwM*mC|IDW*7eq_cBdH=LaXWd z$<8A~1pnPWbj}#J3S^D@vU*Mn$Oh1r?!x4@aQOPHA@!@&&cfkFI` z5-u$xE_&__hc=LcOxhvS*f2@7MBe7>Sva2~SGh|$tm^5lwZgbtWW+|lqEotvU)5mRXWkr0+ z%6yq>p5j?H<_51#j+aIJT%@t0v)tNW5+Dn*oMUEhhe(W!vY&eB4_bQ?nbw!ompGP< zT_!GTiaLuxdC?R|0lf%E_-|oxsrJ(mXzKV{lOAWjHdtNXV52M>a!cMq(0XFFg6E%q zmXL~hr~Pr0int=bOM>=uhd}w>A@Y}NU{6c@yX|UNLy2)-{oBBPfvGY5+ z0v3}GGqie#=WDxdm809~kcA%O?0&kSM|B5db!~9z{WKxRkW;V&2YOz#^C`vSt83g9 zjd>S!{Z*!6f)4%JuA@^DtwT`W@BN)_%B0dS2LtE;jH@Q9C&;JIzr%++GMZ^+b@7QY zmi)${$`D<{S!h)kE=Qb4_Q-37W=Pl*ZAUt}U*svE{K6>`M4(b(@8M)dWw64d4DQ6# zZ_*4hzRW9Q0rm;BU%Sr8C0GU^qxZGg94otw)m<#-jJ$vZMNRD_G*GQXby;qCu#Gt% z4%OhvN9FxnPu5F1HCkU?#78bg=V!cv=X<2dhgP1D$ z+eulVT50A4-|-{~nH!EZ7mHbb6KKY>7yhwXA-9xHVu45eek7VNo4_1jDCGctBvH{E zv$2#)CWF_-_X>yh`;A(nSR8z$0SuWGB3cF)-zI-(S8d@l^ea_bm zAwPXIlN!zQlgn^9`}Uj5K^3ULzYDKF-xtqD5uZj@mj{9F#f)WIjuj~e1AJ{o>FbM@-Un7A zVw==VFOtK>Xc6&q=o2$h^FSBJGcUqPzk<M|pVO=q6;4!Bd z|CksfrNrqPSxYA}^SXI%gIbY`Au`M^8KUbnryI$UhV|N_D=)3eUhwwhbrpkVznvPh{!!F5?egIepN*_EZyaAtZJk)5712` zVT2pN=R~oK6UO72;JmcM@6WT)R(^*g?^|=5SiC?LPY<1TrIo(MuE z^o49&c`M6_NYoITzCf;TVnuB^f95E@$*L{9e+-1{w7&m&fNzr(1A)&Fr8Qn{c>vocY#^SNVN$Azl@r znnzo1$}Cr0Hl=n8Co*QaUm*gK?G;_x#?iqHVKMogn%hSLW4{KVa4)0CvOgBSK^>U` zF%xXsG7?{!JYXHd< zf?Axq8KhA(;lLASmG*7VZJ+VeeQ{P7^ez1K{P7PBixTBIh2J?Ds2wffX5I=i;c19$ z`4qbLU6ISM<-CcK7|_4N1Wm zJoztY3DHmiiN_^f@1E5(Pqn9fvb+R+v&d)hI8L;R?)knzo zybl$;m_9)!5(hRm^ftQ!a)*_Aa0}9)LT;s7{=~%d4+L{}d$LcQ{)vZ!*td`P$UuuZ z!s{3g$zM($lh6^3r4PWt+pUtA=~mc%(rjDU&@+voWpQYA6RRw-ZSb|-jcK*HK_)o!+2xG22grG{8wb@ zAj7KWIYB_pWITPI*H+XF7 zCvp%umakDbZIgreFdK+p5T2WTtnfj?^^pN&T?=zOZ`z=0)!-CAdH}Rso4xji0c>g9 z`o~s)ma7EHgU)D)zYTB~&m}H9N~UVR9MnvRo;|6*{!{2=e9;r3C+m1@HjpbSUqobm z19L-Q)nDiyq+!+a(Q&~~YtXzW%k*6Z0Rm2}RvAU{E_>uv=h3Q>*$8TBQ8&E0RDOqo zBm0f(!9v=h)KK@4iPl)ZhU7O^qu*M%tZ4kvRjn^uEewt2w*>Bgwi_VV(=VKPL_ZkK={OY^z5Lvul@W%TU!$6Y3p69gBk2_q9 zUlnyPiRRe-L$wkO7iDXF?Wmyky^nP(morMrut~`L$Bk-nPgskG?dvrc`+&C&>^+6> zC{trw(*SFdp!uJ5D|DXN|-(O37fq>d=u*Nks^i;#halFJ>O=Iu8s#6|mm@#wzz zd>xgtFgR!=iLR~DxMYX)zwOi2>2xLir!SFnQD366tt!GEuu+4}Is+Tn;)vb!l`*_U z5iDkIAU4@~GBdrFv|qE*u_OXNe7)4M_9e@sV!fuMEAEFeD#f`JXrFN0P8A|`8~aZ+ zHEyvblrjNXYcN{_!zO_Ds;J2cr^R~!H8Eu-L@`Q+B#X!Bx!WFemE0)YBW1Oyn!B4i z;qpokJX61%dE;+Z?8p8fL;cG-&r&WGA0GU5SJEMHtpFgL6{$C=aenhHhx38u)FXm9 z>#JIEUn6tbg-aVuMAK@UQzyWBhfL`XyXFt^8qPOqYBJm#+cjcXwi|I6c~WUcRwYk` z__yJ|#CJ^9x&fx~kPhV{zvJq7WLs+<9m)J2V54cdUoAaP>U=cN$}&HhUe+AF=J2oS zCI7kIa_o7bJodH5cWaRlW2JrdvzuZC4k7W@ihy$G{1 zzESz%Hd&)5paophX`kcW7UTC&XHS~JGMy~ojrz2DoCkTsPceL!V_L+5`LN_LRQb3z z@XxEWM0d9eR!sk^Sf;}-zD;PdKZ7)@4XVf z^s~7V_rUhEx$V=PQ8_fLJ$=>I8jJC_fw7_G$GabHjfWZwjB4$@cy!LTt=6&UFsj=u zJ&%Ui^*@Dr7Ac%v8IHhmw?@72r*Digonun2LTpX@40uHgvmYNcPO5<6tj#r(t5vg7 zh358_b;R$mR*ERFtx|wExwB%KP6nuxL9ijNUr~*RRHLCxu9J!Nz;H^dNrS2L`r9Q0 zz8%maMxTdL?z1i~Kc>Kj<3^k>4qM}QKcy$;TQ6tfcuF~Ge|lN&rj)w(-@1)1<8Vmg z=k6jpT+nXEo4b4MTUWf?&3BL?OrI`H+H~)vbc(}GM2zP@f)a)uulW71hBac^aXpTR zix)0h&cjiim7I+J7A59q-pqD6ert&odT8Gj`oog!c$Im`6y-H-UD~x1gn3JBIv)G! zZ{=zRb45?<*~$mCPO z-ZR7vG4~#FJHaXMqWdzG;zHN-NgnXtMyP!Xo&J2aD&Crre!okzKrAQAy89~d?Hy%} zZ}La5`wVMnloWQjLvc2dyeDr8P{_B&(=hXFTjp-RjOrh?tG_z+rT{(pP4@f6Z%bLW z8IB%iE1x$~x3q>tqxO7^*M<9mF|;uVT<@(=WMn7E8de7Q4^V)dMI={XVO#YVpChyr z$GmU_R!2YkcJm!;Qa8WIg1ylK3g4|qrILr2Olx2qO;wjg(T)o&bfZuFgea#{vFX0Sz*{Sd5!oS)(nZ zu`&eEm$3wZ1_E#+o{`8BM~?tJ+RUS)ISxUAVaw->qOIA1GrT3?;NsladJyGw$oeOi zkBhwA_Rcz|B5X_civa6_gZ1t44KgD!Z~cQd)-xOj%$|nQ%2v9{6I9qWjO3@LI_idt-U8r-=FRLk&W0uCVE9S53Vy zq|Z;nHd%9j^g~OAP+YKp_XM)X(C<;L(=|l2Xg)ZkLPUagiw;C1`esV`%NlR36Tb+o ztBml@Gf6D_j)>~855#qgN{8IP4IAil`kghruRV5RFLass2B_zNB?q=KH5{Yq`8J6G z)t|*8<;9&}AL)ugw&L9MgMtLBEn6QPI_^z2`t1G{ygVM%HM_Ho$tC*4*K_RC`KbmC zBz62lLd7gtEhiMTu)3tk5ajWbTQK5x>hIx2;Jeh|8x_s8ht9RDZ{Xek00No73M}4e zoK#fe+iMN%?fc%m34{B%-hGufp6ZT**@)K^WPMlT{|8vQ4=;>`qO_$^y$b96h#gW9 zc;2TO-Sy7PgxmM-e@bmM+jMU?$X9UkFk9>Ey!kgky8NUQdTk~{m48t?L%5DXig9`! zC9^J^`kDF&Ow6fbpu(GFn^*FoKByGc9~b{2D4x-vnQn^z#lUu;U5}21*s|}EsHbpj zFYaiHhxmmTmSUXHjOh*MX6O@l1I3;d{&>H5CUA9GN}=B7I?w^|?Zo&??I`P0`=%PC zp1uE0KX$H4UQH#HtlZ@>m~r63dfUZBCEZ`Tp5D-lHU*{i!lzt20x^7<8^as6AozB~ z(0RM9*~r5$n8K$&aq}oBsUlUY1TS#__y)uAf@;n67pYlGaL{8$)jVc~a>Z7mFp}%& z)mA&?{CMHmc6Y~aj2)3$aa?m1ys7kbuC_TfIga9s!ET5P|KAbW*hZ#B$I->yV@%j# z)RTuzNjHfnR%=Uz^|)j&#Ay{RK1PGbaAL$_+c8u?VAk0L6Ly~FD{XjxRJBGJ|GHN0 zi-Zgmmri+)rsf%{ly93Tt0rYPaC@nGzRtC`ML94wTOcIOFb@6xis!;efXs>_mWn&1 z-q<@v68x!d?q?b3F31J)uGz-5zj@_g?)T361+r{iV&~mm-*4g{RnX5k>^n|ej;=Xa zF67PTRF%%Vmd1Y-^_7xa_sFhQ33?sC8()Hk^Z4Uq%2%Rv&~3kJ{Z4Zdc^=`!ccNN) zenc8dAMsatEfzH|m7FeXixj#IQnW25?x1M+fH%i5b$tmJp(!c_0t?1W#R^icuB_&T1CnArzRmBC?@jvJ) zPqJ?7f7{+BWo!_E3zrDq@k>U^7tmo)LTui76U*4vhv zL3~~U-QDdFxpg_e$QX8ove2}{u>F434mD`ThYu|(*`W@AB2*6g9nsz>)xC523O`|o|}?++CNL|X}rd4!P3OOLvb9(84BK2J3G(L|AUjqH-LR0 z<{psiieszfV*ShzDP1Lb4xe?N){dak*2Xg(fA*%d+x=eUY-{xAH-pi3{u_EOW0`SV z#w|=-rxe+_+$$`J+&i5wg_jQf4^kn2Ad36D>qinyeYA_<_$w0HPc`i zk;u%HOP*1vH_1E`R?P!EOkfte0cz7CsWwuh<|p;u?vDG%z*)pqSRrPu82kwuhTcB+ zlMmcSk0TL9)MBl<&^2}Rb3(wETz!s(E&JfB6S90cL@*{tLfI3$i<1^pAmDH^Oz-k> zh4h&<2tMa^e}fj21W@ibW7Y^j0$V(+++XDy>R}g^_Gg~VwsTIjQ@3~+d+94S5J?tO z4fwAvqhDXoixHP=XBPNghrGSr`nmGEPVK$P+&@iSCr*ukx@9R2sU}`}g>ur!E)O6D zzZ}NBfRd~UqwJmjhGLK#5&2rw%xR={)sKjSmQ94PB+H|%p*d|EOcDt`bvkI`Uw|h^wCr9|Nys15wOQhO`i>wrL=dzf z`)eXkb@kTA=S$Vga6ikkidhwxg0G8oWf3r`UEs;^hm>Pr4~=@VAi2T>h^__FM8B=Z z80H{pXNstd-UhqACsQx$WC5ohPB|n+(TdNu+Vg)5f{4iJ@Gq$K*!5u(S0)cp1E>W! zBuwAFW?A+41zPK@<1?O>VTr$JoBKE;*i-Fbn~O!}A%*^*AwKb62Ryg_ET1-KsH6qe zhge;i;3fjU;6cf;7-S)X{&Pr!6for5f^}aop<)&Q5hQJ#8~O{l+4*6(Gos%3VD%TU zJS-lT_RKF&JpRYSI~3E)5phID+{TeQk`O$zw%_RRkvi6Ax~bWEe+abKlf_pA8@n1= z%)Fdjd8D4Li934Mn+E>)wZNncdShVHatoFh%$U`G+9C$BCsz{`e)&t-UgV59E}r;Q zZwb!pTr>9*7!-dq(sI$l2246?#k#L4ZzfDl8iWln&^2SOmX^13szWrV%~Z8lyHF;& z{%T{hodHj%o74IG12b@2EJzjhbkjggM*`Tz^1_qDNhNc2dfQJW zHFfaM&Wxyh!|Is5Q%3@qf1H^=u6?s6gNHY)ej+9;QMyaX0?Kg$}Ys{!mng2-y{KSFHN%4^gVt-`;Jod|2VPiY=d}eT0QI55t)w6WkAK?db)J# zfK>owFWkhH-Sy(C!;rS;jq!^zm9)H`b`CumVP9Oh6-sD2?Ldg!v^4fP#Z!z4_SKFy zD>@EI@%OJ}N1)%TWA}bF>TlYgr_$AR&FX<05HUK1E}?cbNe~MHx}>4Ga)qnDY0$QX zUh_xai~{D)v0Z8(({;RL(YW*?$qd8yr7dEXTZ+S2yE$)KWDL*rHR+6W3)Jgh;3^9y zF@IeIM~FIx@@t`e5RSaCvWALvb%+VNDnsV83Z)lwAr!()0qq&*ma65lY3`{7#KNdz z$J2^WKRO!7y2@kJrls>sO_c8`V=8hrEKCvD*T;BzE1Q zZ4(PAbwD|$r-cD*F-{7P{BtprLpXz*8(ZN3UXsQQLnJ;%VbQcsz?Ac?%`xS(;x~aS zetGpS`dJ=SEM7)k#s#E6C}~W64JV&P!2d6&d+Lwq*KnUcrl@E2^o5+1l4OOrQNaHI DfrWEq literal 0 HcmV?d00001 diff --git a/rsshub/templates/errors/400.html b/rsshub/templates/errors/400.html new file mode 100644 index 0000000..b01e20c --- /dev/null +++ b/rsshub/templates/errors/400.html @@ -0,0 +1,9 @@ +{% extends 'layout.html' %} + +{% block title %}400 错误{% endblock %} + +{% block content %} +
+

400 Bad Request

+
+{% endblock %} \ No newline at end of file diff --git a/rsshub/templates/errors/404.html b/rsshub/templates/errors/404.html new file mode 100644 index 0000000..9480d26 --- /dev/null +++ b/rsshub/templates/errors/404.html @@ -0,0 +1,9 @@ +{% extends 'layout.html' %} + +{% block title %}404 错误{% endblock %} + +{% block content %} +
+

404 Not Found

+
+{% endblock %} \ No newline at end of file diff --git a/rsshub/templates/errors/500.html b/rsshub/templates/errors/500.html new file mode 100644 index 0000000..8e4546a --- /dev/null +++ b/rsshub/templates/errors/500.html @@ -0,0 +1,9 @@ +{% extends 'layout.html' %} + +{% block title %}500 错误{% endblock %} + +{% block content %} +
+

服务器出错

+
+{% endblock %} \ No newline at end of file diff --git a/rsshub/templates/layout.html b/rsshub/templates/layout.html new file mode 100644 index 0000000..34678ba --- /dev/null +++ b/rsshub/templates/layout.html @@ -0,0 +1,69 @@ +{% from 'bootstrap/nav.html' import render_nav_item %} + + + + + {% block head %} + + + {% block title %}{% endblock title %} + + + {% block styles %} + {{ bootstrap.load_css() }} + + + {% endblock styles %} + {% endblock head %} + + + + {% block nav %} + + {% endblock nav %} +
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} + {% block content %}{% endblock %} + {% block footer %} + + {% endblock footer %} +
+ {% block scripts %} + {{ bootstrap.load_js() }} + {{ moment.include_moment() }} + {{ moment.locale('zh-cn') }} + {% endblock %} + + + \ No newline at end of file diff --git a/rsshub/templates/main/atom.xml b/rsshub/templates/main/atom.xml new file mode 100644 index 0000000..017b7f8 --- /dev/null +++ b/rsshub/templates/main/atom.xml @@ -0,0 +1,22 @@ + + + {{config['SITE_NAME']}} + {{config['EMAIL']}} + zh-cn + {{link}} + <![CDATA[{{title|safe}}]]> + + + + + {% for item in items %} + + {{item.link}} + <![CDATA[{{item.title|safe}}]]> + {{item.pubDate}} + {{item.pubDate}} + + + + {% endfor %} + \ No newline at end of file diff --git a/rsshub/templates/main/feeds.html b/rsshub/templates/main/feeds.html new file mode 100644 index 0000000..88e808a --- /dev/null +++ b/rsshub/templates/main/feeds.html @@ -0,0 +1,11 @@ +{% extends "layout.html" %} + +{% block title %}All Feeds{% endblock title %} + +{% block content %} +
+ {% for rule in rules %} + {{rule.endpoint[5:]}} + {% endfor %} +
+{% endblock content %} \ No newline at end of file diff --git a/rsshub/templates/main/index.html b/rsshub/templates/main/index.html new file mode 100644 index 0000000..95dca1d --- /dev/null +++ b/rsshub/templates/main/index.html @@ -0,0 +1,13 @@ +{% extends 'layout.html' %} + +{% block title %}Welcome to RSShub!{% endblock title %} + +{% block content %} +
+

Welcome to RSSHub!

+

If you see this page, the RSSHub is successfully installed and working.

+

+ View All Feeds +

+
+{% endblock %} \ No newline at end of file diff --git a/rsshub/utils.py b/rsshub/utils.py new file mode 100644 index 0000000..b1cd5e8 --- /dev/null +++ b/rsshub/utils.py @@ -0,0 +1,25 @@ +from flask import Response +import requests +from parsel import Selector + +DEFAULT_HEADERS = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'} + + +class XMLResponse(Response): + def __init__(self, response, **kwargs): + if 'mimetype' not in kwargs and 'contenttype' not in kwargs: + if response.startswith('