Code Duplication    Length = 21-29 lines in 25 locations

comics.py 25 locations

@@ 1861-1887 (lines=27) @@
1858
1859
class PicturesInBoxes(GenericNavigableComic):
1860
    """Class to retrieve Pictures In Boxes comics."""
1861
    # Also on https://picturesinboxescomic.tumblr.com
1862
    name = 'picturesinboxes'
1863
    long_name = 'Pictures in Boxes'
1864
    url = 'http://www.picturesinboxes.com'
1865
    get_navi_link = get_a_navi_navinext
1866
    get_first_comic_link = simulate_first_link
1867
    first_url = 'http://www.picturesinboxes.com/2013/10/26/tetris/'
1868
1869
    @classmethod
1870
    def get_comic_info(cls, soup, link):
1871
        """Get information about a particular comics."""
1872
        title = soup.find('h2', class_='post-title').string
1873
        author = soup.find("span", class_="post-author").find("a").string
1874
        date_str = soup.find('span', class_='post-date').string
1875
        day = string_to_date(date_str, '%B %d, %Y')
1876
        imgs = soup.find('div', class_='comicpane').find_all('img')
1877
        assert imgs
1878
        assert all(i['title'] == i['alt'] == title for i in imgs)
1879
        return {
1880
            'day': day.day,
1881
            'month': day.month,
1882
            'year': day.year,
1883
            'img': [i['src'] for i in imgs],
1884
            'title': title,
1885
            'author': author,
1886
        }
1887
1888
1889
class Penmen(GenericNavigableComic):
1890
    """Class to retrieve Penmen comics."""
@@ 928-954 (lines=27) @@
925
926
class ImogenQuest(GenericNavigableComic):
927
    """Class to retrieve Imogen Quest comics."""
928
    # Also on http://imoquest.tumblr.com
929
    name = 'imogen'
930
    long_name = 'Imogen Quest'
931
    url = 'http://imogenquest.net'
932
    get_first_comic_link = get_div_navfirst_a
933
    get_navi_link = get_a_rel_next
934
935
    @classmethod
936
    def get_comic_info(cls, soup, link):
937
        """Get information about a particular comics."""
938
        title = soup.find('h2', class_='post-title').string
939
        author = soup.find("span", class_="post-author").find("a").string
940
        date_str = soup.find('span', class_='post-date').string
941
        day = string_to_date(date_str, '%B %d, %Y')
942
        imgs = soup.find('div', class_='comicpane').find_all('img')
943
        assert all(i['alt'] == i['title'] for i in imgs)
944
        title2 = imgs[0]['title']
945
        return {
946
            'day': day.day,
947
            'month': day.month,
948
            'year': day.year,
949
            'img': [i['src'] for i in imgs],
950
            'title': title,
951
            'title2': title2,
952
            'author': author,
953
        }
954
955
956
class MyExtraLife(GenericNavigableComic):
957
    """Class to retrieve My Extra Life comics."""
@@ 2534-2559 (lines=26) @@
2531
    """Class to retrieve Biter Comics."""
2532
    name = "biter"
2533
    long_name = "Biter Comics"
2534
    url = "http://www.bitercomics.com"
2535
    get_first_comic_link = get_a_navi_navifirst
2536
    get_navi_link = get_link_rel_next
2537
2538
    @classmethod
2539
    def get_comic_info(cls, soup, link):
2540
        """Get information about a particular comics."""
2541
        title = soup.find("h1", class_="entry-title").string
2542
        author = soup.find("span", class_="author vcard").find("a").string
2543
        date_str = soup.find("span", class_="entry-date").string
2544
        day = string_to_date(date_str, "%B %d, %Y")
2545
        imgs = soup.find("div", id="comic").find_all("img")
2546
        assert all(i['alt'] == i['title'] for i in imgs)
2547
        assert len(imgs) == 1
2548
        alt = imgs[0]['alt']
2549
        return {
2550
            'img': [i['src'] for i in imgs],
2551
            'title': title,
2552
            'alt': alt,
2553
            'author': author,
2554
            'day': day.day,
2555
            'month': day.month,
2556
            'year': day.year
2557
        }
2558
2559
2560
class TheAwkwardYeti(GenericNavigableComic):
2561
    """Class to retrieve The Awkward Yeti comics."""
2562
    # Also on http://www.gocomics.com/the-awkward-yeti
@@ 2788-2812 (lines=25) @@
2785
        desc = soup.find('meta', property='og:description')['content']
2786
        title = soup.find('meta', property='og:title')['content']
2787
        imgs = soup.find('div', class_='entry-content').find_all('img')
2788
        title2 = ' '.join(i.get('title', '') for i in imgs)
2789
        return {
2790
            'title': title,
2791
            'title2': title2,
2792
            'description': desc,
2793
            'img': [urljoin_wrapper(cls.url, convert_iri_to_plain_ascii_uri(i['src'])) for i in imgs],
2794
        }
2795
2796
2797
class CommitStripFr(GenericCommitStrip):
2798
    """Class to retrieve Commit Strips in French."""
2799
    name = 'commit_fr'
2800
    long_name = 'Commit Strip (Fr)'
2801
    url = 'http://www.commitstrip.com/fr'
2802
    _categories = ('FRANCAIS', )
2803
    first_url = 'http://www.commitstrip.com/fr/2012/02/22/interview/'
2804
2805
2806
class CommitStripEn(GenericCommitStrip):
2807
    """Class to retrieve Commit Strips in English."""
2808
    name = 'commit_en'
2809
    long_name = 'Commit Strip (En)'
2810
    url = 'http://www.commitstrip.com/en'
2811
    first_url = 'http://www.commitstrip.com/en/2012/02/22/interview/'
2812
2813
2814
class GenericBoumerie(GenericNavigableComic):
2815
    """Generic class to retrieve Boumeries comics in different languages."""
@@ 2476-2500 (lines=25) @@
2473
class GerbilWithAJetpack(GenericNavigableComic):
2474
    """Class to retrieve GerbilWithAJetpack comics."""
2475
    name = 'gerbil'
2476
    long_name = 'Gerbil With A Jetpack'
2477
    url = 'http://gerbilwithajetpack.com'
2478
    get_first_comic_link = get_a_navi_navifirst
2479
    get_navi_link = get_a_rel_next
2480
2481
    @classmethod
2482
    def get_comic_info(cls, soup, link):
2483
        """Get information about a particular comics."""
2484
        title = soup.find('h2', class_='post-title').string
2485
        author = soup.find("span", class_="post-author").find("a").string
2486
        date_str = soup.find("span", class_="post-date").string
2487
        day = string_to_date(date_str, "%B %d, %Y")
2488
        imgs = soup.find("div", id="comic").find_all("img")
2489
        alt = imgs[0]['alt']
2490
        assert all(i['alt'] == i['title'] == alt for i in imgs)
2491
        return {
2492
            'img': [i['src'] for i in imgs],
2493
            'title': title,
2494
            'alt': alt,
2495
            'author': author,
2496
            'day': day.day,
2497
            'month': day.month,
2498
            'year': day.year
2499
        }
2500
2501
2502
class EveryDayBlues(GenericEmptyComic, GenericNavigableComic):
2503
    """Class to retrieve EveryDayBlues Comics."""
@@ 1749-1773 (lines=25) @@
1746
1747
class MouseBearComedy(GenericNavigableComic):
1748
    """Class to retrieve Mouse Bear Comedy comics."""
1749
    # Also on http://mousebearcomedy.tumblr.com
1750
    name = 'mousebear'
1751
    long_name = 'Mouse Bear Comedy'
1752
    url = 'http://www.mousebearcomedy.com'
1753
    get_first_comic_link = get_a_navi_navifirst
1754
    get_navi_link = get_a_navi_comicnavnext_navinext
1755
1756
    @classmethod
1757
    def get_comic_info(cls, soup, link):
1758
        """Get information about a particular comics."""
1759
        title = soup.find('h2', class_='post-title').string
1760
        author = soup.find("span", class_="post-author").find("a").string
1761
        date_str = soup.find("span", class_="post-date").string
1762
        day = string_to_date(date_str, '%B %d, %Y')
1763
        imgs = soup.find("div", id="comic").find_all("img")
1764
        assert all(i['alt'] == i['title'] == title for i in imgs)
1765
        return {
1766
            'day': day.day,
1767
            'month': day.month,
1768
            'year': day.year,
1769
            'img': [i['src'] for i in imgs],
1770
            'title': title,
1771
            'author': author,
1772
        }
1773
1774
1775
class BigFootJustice(GenericNavigableComic):
1776
    """Class to retrieve Big Foot Justice comics."""
@@ 1157-1180 (lines=24) @@
1154
1155
class AmazingSuperPowers(GenericNavigableComic):
1156
    """Class to retrieve Amazing Super Powers comics."""
1157
    name = 'asp'
1158
    long_name = 'Amazing Super Powers'
1159
    url = 'http://www.amazingsuperpowers.com'
1160
    get_first_comic_link = get_a_navi_navifirst
1161
    get_navi_link = get_a_navi_navinext
1162
1163
    @classmethod
1164
    def get_comic_info(cls, soup, link):
1165
        """Get information about a particular comics."""
1166
        author = soup.find("span", class_="post-author").find("a").string
1167
        date_str = soup.find('span', class_='post-date').string
1168
        day = string_to_date(date_str, "%B %d, %Y")
1169
        imgs = soup.find('div', id='comic').find_all('img')
1170
        title = ' '.join(i['title'] for i in imgs)
1171
        assert all(i['alt'] == i['title'] for i in imgs)
1172
        return {
1173
            'title': title,
1174
            'author': author,
1175
            'img': [img['src'] for img in imgs],
1176
            'day': day.day,
1177
            'month': day.month,
1178
            'year': day.year
1179
        }
1180
1181
1182
class ToonHole(GenericNavigableComic):
1183
    """Class to retrieve Toon Holes comics."""
@@ 674-697 (lines=24) @@
671
672
class OneOneOneOneComic(GenericEmptyComic, GenericNavigableComic):
673
    """Class to retrieve 1111 Comics."""
674
    # Also on http://comics1111.tumblr.com
675
    # Also on https://tapastic.com/series/1111-Comics
676
    name = '1111'
677
    long_name = '1111 Comics'
678
    url = 'http://www.1111comics.me'
679
    _categories = ('ONEONEONEONE', )
680
    get_first_comic_link = get_div_navfirst_a
681
    get_navi_link = get_link_rel_next
682
683
    @classmethod
684
    def get_comic_info(cls, soup, link):
685
        """Get information about a particular comics."""
686
        title = soup.find('h1', class_='comic-title').find('a').string
687
        date_str = soup.find('header', class_='comic-meta entry-meta').find('a').string
688
        day = string_to_date(date_str, "%B %d, %Y")
689
        imgs = soup.find_all('meta', property='og:image')
690
        return {
691
            'title': title,
692
            'month': day.month,
693
            'year': day.year,
694
            'day': day.day,
695
            'img': [i['content'] for i in imgs],
696
        }
697
698
699
class AngryAtNothing(GenericEmptyComic, GenericNavigableComic):
700
    """Class to retrieve Angry at Nothing comics."""
@@ 902-924 (lines=23) @@
899
900
class TheGentlemanArmchair(GenericNavigableComic):
901
    """Class to retrieve The Gentleman Armchair comics."""
902
    name = 'gentlemanarmchair'
903
    long_name = 'The Gentleman Armchair'
904
    url = 'http://thegentlemansarmchair.com'
905
    get_first_comic_link = get_a_navi_navifirst
906
    get_navi_link = get_link_rel_next
907
908
    @classmethod
909
    def get_comic_info(cls, soup, link):
910
        """Get information about a particular comics."""
911
        title = soup.find('h2', class_='post-title').string
912
        author = soup.find("span", class_="post-author").find("a").string
913
        date_str = soup.find('span', class_='post-date').string
914
        day = string_to_date(date_str, "%B %d, %Y")
915
        imgs = soup.find('div', id='comic').find_all('img')
916
        return {
917
            'img': [i['src'] for i in imgs],
918
            'title': title,
919
            'author': author,
920
            'month': day.month,
921
            'year': day.year,
922
            'day': day.day,
923
        }
924
925
926
class ImogenQuest(GenericNavigableComic):
927
    """Class to retrieve Imogen Quest comics."""
@@ 701-722 (lines=22) @@
698
699
class AngryAtNothing(GenericEmptyComic, GenericNavigableComic):
700
    """Class to retrieve Angry at Nothing comics."""
701
    # Also on http://tapastic.com/series/Comics-yeah-definitely-comics-
702
    # Also on http://angryatnothing.tumblr.com
703
    name = 'angry'
704
    long_name = 'Angry At Nothing'
705
    url = 'http://www.angryatnothing.net'
706
    get_first_comic_link = get_div_navfirst_a
707
    get_navi_link = get_a_rel_next
708
709
    @classmethod
710
    def get_comic_info(cls, soup, link):
711
        """Get information about a particular comics."""
712
        title = soup.find('h1', class_='comic-title').find('a').string
713
        date_str = soup.find('header', class_='comic-meta entry-meta').find('a').string
714
        day = string_to_date(date_str, "%B %d, %Y")
715
        imgs = soup.find_all('meta', property='og:image')
716
        return {
717
            'title': title,
718
            'month': day.month,
719
            'year': day.year,
720
            'day': day.day,
721
            'img': [i['content'] for i in imgs],
722
        }
723
724
725
class NeDroid(GenericNavigableComic):
@@ 2645-2673 (lines=29) @@
2642
    """Class to retrieve Last Place Comics."""
2643
    name = 'lastplace'
2644
    long_name = 'Last Place Comics'
2645
    url = "http://lastplacecomics.com"
2646
    get_first_comic_link = get_a_comicnavbase_comicnavfirst
2647
    get_navi_link = get_link_rel_next
2648
2649
    @classmethod
2650
    def get_comic_info(cls, soup, link):
2651
        """Get information about a particular comics."""
2652
        title = soup.find('h2', class_='post-title').string
2653
        author = soup.find("span", class_="post-author").find("a").string
2654
        date_str = soup.find("span", class_="post-date").string
2655
        day = string_to_date(date_str, "%B %d, %Y")
2656
        imgs = soup.find("div", id="comic").find_all("img")
2657
        assert all(i['alt'] == i['title'] for i in imgs)
2658
        assert len(imgs) <= 1
2659
        alt = imgs[0]['alt'] if imgs else ""
2660
        return {
2661
            'img': [i['src'] for i in imgs],
2662
            'title': title,
2663
            'alt': alt,
2664
            'author': author,
2665
            'day': day.day,
2666
            'month': day.month,
2667
            'year': day.year
2668
        }
2669
2670
2671
class TalesOfAbsurdity(GenericNavigableComic):
2672
    """Class to retrieve Tales Of Absurdity comics."""
2673
    # Also on http://tapastic.com/series/Tales-Of-Absurdity
2674
    # Also on http://talesofabsurdity.tumblr.com
2675
    name = 'absurdity'
2676
    long_name = 'Tales of Absurdity'
@@ 2869-2895 (lines=27) @@
2866
    long_name = 'Unearthed Comics'
2867
    url = 'http://unearthedcomics.com'
2868
    _categories = ('UNEARTHED', )
2869
    get_navi_link = get_link_rel_next
2870
    get_first_comic_link = simulate_first_link
2871
    first_url = 'http://unearthedcomics.com/comics/world-with-turn-signals/'
2872
2873
    @classmethod
2874
    def get_comic_info(cls, soup, link):
2875
        """Get information about a particular comics."""
2876
        short_url = soup.find('link', rel='shortlink')['href']
2877
        title_elt = soup.find('h1') or soup.find('h2')
2878
        title = title_elt.string if title_elt else ""
2879
        desc = soup.find('meta', property='og:description')
2880
        date_str = soup.find('time', class_='published updated hidden')['datetime']
2881
        day = string_to_date(date_str, "%Y-%m-%d")
2882
        post = soup.find('div', class_="entry content entry-content type-portfolio")
2883
        imgs = post.find_all('img')
2884
        return {
2885
            'title': title,
2886
            'description': desc,
2887
            'url2': short_url,
2888
            'img': [i['src'] for i in imgs],
2889
            'month': day.month,
2890
            'year': day.year,
2891
            'day': day.day,
2892
        }
2893
2894
2895
class Optipess(GenericNavigableComic):
2896
    """Class to retrieve Optipess comics."""
2897
    name = 'optipess'
2898
    long_name = 'Optipess'
@@ 2504-2530 (lines=27) @@
2501
2502
class EveryDayBlues(GenericEmptyComic, GenericNavigableComic):
2503
    """Class to retrieve EveryDayBlues Comics."""
2504
    name = "blues"
2505
    long_name = "Every Day Blues"
2506
    url = "http://everydayblues.net"
2507
    get_first_comic_link = get_a_navi_navifirst
2508
    get_navi_link = get_link_rel_next
2509
2510
    @classmethod
2511
    def get_comic_info(cls, soup, link):
2512
        """Get information about a particular comics."""
2513
        title = soup.find("h2", class_="post-title").string
2514
        author = soup.find("span", class_="post-author").find("a").string
2515
        date_str = soup.find("span", class_="post-date").string
2516
        day = string_to_date(date_str, "%d. %B %Y", "de_DE.utf8")
2517
        imgs = soup.find("div", id="comic").find_all("img")
2518
        assert all(i['alt'] == i['title'] == title for i in imgs)
2519
        assert len(imgs) <= 1
2520
        return {
2521
            'img': [i['src'] for i in imgs],
2522
            'title': title,
2523
            'author': author,
2524
            'day': day.day,
2525
            'month': day.month,
2526
            'year': day.year
2527
        }
2528
2529
2530
class BiterComics(GenericNavigableComic):
2531
    """Class to retrieve Biter Comics."""
2532
    name = "biter"
2533
    long_name = "Biter Comics"
@@ 2006-2032 (lines=27) @@
2003
2004
class CompletelySeriousComics(GenericNavigableComic):
2005
    """Class to retrieve Completely Serious comics."""
2006
    name = 'completelyserious'
2007
    long_name = 'Completely Serious Comics'
2008
    url = 'http://completelyseriouscomics.com'
2009
    get_first_comic_link = get_a_navi_navifirst
2010
    get_navi_link = get_a_navi_navinext
2011
2012
    @classmethod
2013
    def get_comic_info(cls, soup, link):
2014
        """Get information about a particular comics."""
2015
        title = soup.find('h2', class_='post-title').string
2016
        author = soup.find('span', class_='post-author').contents[1].string
2017
        date_str = soup.find('span', class_='post-date').string
2018
        day = string_to_date(date_str, '%B %d, %Y')
2019
        imgs = soup.find('div', class_='comicpane').find_all('img')
2020
        assert imgs
2021
        alt = imgs[0]['title']
2022
        assert all(i['title'] == i['alt'] == alt for i in imgs)
2023
        return {
2024
            'month': day.month,
2025
            'year': day.year,
2026
            'day': day.day,
2027
            'img': [i['src'] for i in imgs],
2028
            'title': title,
2029
            'alt': alt,
2030
            'author': author,
2031
        }
2032
2033
2034
class PoorlyDrawnLines(GenericListableComic):
2035
    """Class to retrieve Poorly Drawn Lines comics."""
@@ 2677-2702 (lines=26) @@
2674
    # Also on http://talesofabsurdity.tumblr.com
2675
    name = 'absurdity'
2676
    long_name = 'Tales of Absurdity'
2677
    url = 'http://talesofabsurdity.com'
2678
    _categories = ('ABSURDITY', )
2679
    get_first_comic_link = get_a_navi_navifirst
2680
    get_navi_link = get_a_navi_comicnavnext_navinext
2681
2682
    @classmethod
2683
    def get_comic_info(cls, soup, link):
2684
        """Get information about a particular comics."""
2685
        title = soup.find('h2', class_='post-title').string
2686
        author = soup.find("span", class_="post-author").find("a").string
2687
        date_str = soup.find("span", class_="post-date").string
2688
        day = string_to_date(date_str, "%B %d, %Y")
2689
        imgs = soup.find("div", id="comic").find_all("img")
2690
        assert all(i['alt'] == i['title'] for i in imgs)
2691
        alt = imgs[0]['alt'] if imgs else ""
2692
        return {
2693
            'img': [i['src'] for i in imgs],
2694
            'title': title,
2695
            'alt': alt,
2696
            'author': author,
2697
            'day': day.day,
2698
            'month': day.month,
2699
            'year': day.year
2700
        }
2701
2702
2703
class EndlessOrigami(GenericEmptyComic, GenericNavigableComic):
2704
    """Class to retrieve Endless Origami Comics."""
2705
    name = "origami"
@@ 2218-2243 (lines=26) @@
2215
2216
class HappleTea(GenericNavigableComic):
2217
    """Class to retrieve Happle Tea Comics."""
2218
    name = 'happletea'
2219
    long_name = 'Happle Tea'
2220
    url = 'http://www.happletea.com'
2221
    get_first_comic_link = get_a_navi_navifirst
2222
    get_navi_link = get_link_rel_next
2223
2224
    @classmethod
2225
    def get_comic_info(cls, soup, link):
2226
        """Get information about a particular comics."""
2227
        imgs = soup.find('div', id='comic').find_all('img')
2228
        post = soup.find('div', class_='post-content')
2229
        title = post.find('h2', class_='post-title').string
2230
        author = post.find('a', rel='author').string
2231
        date_str = post.find('span', class_='post-date').string
2232
        day = string_to_date(date_str, "%B %d, %Y")
2233
        assert all(i['alt'] == i['title'] for i in imgs)
2234
        return {
2235
            'title': title,
2236
            'img': [i['src'] for i in imgs],
2237
            'alt': ''.join(i['alt'] for i in imgs),
2238
            'month': day.month,
2239
            'year': day.year,
2240
            'day': day.day,
2241
            'author': author,
2242
        }
2243
2244
2245
class RockPaperScissors(GenericNavigableComic):
2246
    """Class to retrieve Rock Paper Scissors comics."""
@@ 1891-1916 (lines=26) @@
1888
1889
class Penmen(GenericNavigableComic):
1890
    """Class to retrieve Penmen comics."""
1891
    name = 'penmen'
1892
    long_name = 'Penmen'
1893
    url = 'http://penmen.com'
1894
    get_navi_link = get_link_rel_next
1895
    get_first_comic_link = simulate_first_link
1896
    first_url = 'http://penmen.com/index.php/2016/09/12/penmen-announces-grin-big-brand-clothing/'
1897
1898
    @classmethod
1899
    def get_comic_info(cls, soup, link):
1900
        """Get information about a particular comics."""
1901
        title = soup.find('title').string
1902
        imgs = soup.find('div', class_='entry-content').find_all('img')
1903
        short_url = soup.find('link', rel='shortlink')['href']
1904
        tags = ' '.join(t.string for t in soup.find_all('a', rel='tag'))
1905
        date_str = soup.find('time')['datetime'][:10]
1906
        day = string_to_date(date_str, "%Y-%m-%d")
1907
        return {
1908
            'title': title,
1909
            'short_url': short_url,
1910
            'img': [i['src'] for i in imgs],
1911
            'tags': tags,
1912
            'month': day.month,
1913
            'year': day.year,
1914
            'day': day.day,
1915
        }
1916
1917
1918
class TheDoghouseDiaries(GenericNavigableComic):
1919
    """Class to retrieve The Dog House Diaries comics."""
@@ 1832-1857 (lines=26) @@
1829
1830
class SafelyEndangered(GenericNavigableComic):
1831
    """Class to retrieve Safely Endangered comics."""
1832
    # Also on http://tumblr.safelyendangered.com
1833
    name = 'endangered'
1834
    long_name = 'Safely Endangered'
1835
    url = 'http://www.safelyendangered.com'
1836
    get_navi_link = get_link_rel_next
1837
    get_first_comic_link = simulate_first_link
1838
    first_url = 'http://www.safelyendangered.com/comic/ignored/'
1839
1840
    @classmethod
1841
    def get_comic_info(cls, soup, link):
1842
        """Get information about a particular comics."""
1843
        title = soup.find('h2', class_='post-title').string
1844
        date_str = soup.find('span', class_='post-date').string
1845
        day = string_to_date(date_str, '%B %d, %Y')
1846
        imgs = soup.find('div', id='comic').find_all('img')
1847
        alt = imgs[0]['alt']
1848
        assert all(i['alt'] == i['title'] for i in imgs)
1849
        return {
1850
            'day': day.day,
1851
            'month': day.month,
1852
            'year': day.year,
1853
            'img': [i['src'] for i in imgs],
1854
            'title': title,
1855
            'alt': alt,
1856
        }
1857
1858
1859
class PicturesInBoxes(GenericNavigableComic):
1860
    """Class to retrieve Pictures In Boxes comics."""
@@ 2346-2370 (lines=25) @@
2343
    def get_url_from_archive_element(cls, tr):
2344
        """Get url corresponding to an archive element."""
2345
        _, td_comic, td_date, _ = tr.find_all('td')
2346
        link = td_comic.find('a')
2347
        return urljoin_wrapper(cls.url, link['href'])
2348
2349
    @classmethod
2350
    def get_comic_info(cls, soup, tr):
2351
        """Get information about a particular comics."""
2352
        td_num, td_comic, td_date, _ = tr.find_all('td')
2353
        num = int(td_num.string)
2354
        link = td_comic.find('a')
2355
        title = link.string
2356
        imgs = soup.find_all('img', id='comic_image')
2357
        date_str = td_date.string
2358
        day = string_to_date(remove_st_nd_rd_th_from_date(date_str), "%B %d, %Y, %I:%M %p")
2359
        assert len(imgs) == 1
2360
        assert all(i.get('alt') == i.get('title') for i in imgs)
2361
        return {
2362
            'num': num,
2363
            'title': title,
2364
            'alt': imgs[0].get('alt', ''),
2365
            'img': [i['src'] for i in imgs],
2366
            'month': day.month,
2367
            'year': day.year,
2368
            'day': day.day,
2369
        }
2370
2371
2372
class LonnieMillsap(GenericNavigableComic):
2373
    """Class to retrieve Lonnie Millsap's comics."""
@@ 2094-2118 (lines=25) @@
2091
2092
class ChuckleADuck(GenericNavigableComic):
2093
    """Class to retrieve Chuckle-A-Duck comics."""
2094
    name = 'chuckleaduck'
2095
    long_name = 'Chuckle-A-duck'
2096
    url = 'http://chuckleaduck.com'
2097
    get_first_comic_link = get_div_navfirst_a
2098
    get_navi_link = get_link_rel_next
2099
2100
    @classmethod
2101
    def get_comic_info(cls, soup, link):
2102
        """Get information about a particular comics."""
2103
        date_str = soup.find('span', class_='post-date').string
2104
        day = string_to_date(remove_st_nd_rd_th_from_date(date_str), "%B %d, %Y")
2105
        author = soup.find('span', class_='post-author').string
2106
        div = soup.find('div', id='comic')
2107
        imgs = div.find_all('img') if div else []
2108
        title = imgs[0]['title'] if imgs else ""
2109
        assert all(i['title'] == i['alt'] == title for i in imgs)
2110
        return {
2111
            'month': day.month,
2112
            'year': day.year,
2113
            'day': day.day,
2114
            'img': [i['src'] for i in imgs],
2115
            'title': title,
2116
            'author': author,
2117
        }
2118
2119
2120
class DepressedAlien(GenericNavigableComic):
2121
    """Class to retrieve Depressed Alien Comics."""
@@ 3156-3179 (lines=24) @@
3153
    long_name = 'Sheldon Comics'
3154
    url = 'http://www.sheldoncomics.com'
3155
3156
    @classmethod
3157
    def get_first_comic_link(cls):
3158
        """Get link to first comics."""
3159
        return get_soup_at_url(cls.url).find("a", id="nav-first")
3160
3161
    @classmethod
3162
    def get_navi_link(cls, last_soup, next_):
3163
        """Get link to next or previous comic."""
3164
        for link in last_soup.find_all("a", id="nav-next" if next_ else "nav-prev"):
3165
            if link['href'] != 'http://www.sheldoncomics.com':
3166
                return link
3167
        return None
3168
3169
    @classmethod
3170
    def get_comic_info(cls, soup, link):
3171
        """Get information about a particular comics."""
3172
        imgs = soup.find("div", id="comic-foot").find_all("img")
3173
        assert all(i['alt'] == i['title'] for i in imgs)
3174
        assert len(imgs) == 1
3175
        title = imgs[0]['title']
3176
        return {
3177
            'title': title,
3178
            'img': [i['src'] for i in imgs],
3179
        }
3180
3181
3182
class Ubertool(GenericNavigableComic):
@@ 648-670 (lines=23) @@
645
646
class PenelopeBagieu(GenericNavigableComic):
647
    """Class to retrieve comics from Penelope Bagieu's blog."""
648
    name = 'bagieu'
649
    long_name = 'Ma vie est tout a fait fascinante (Bagieu)'
650
    url = 'http://www.penelope-jolicoeur.com'
651
    _categories = ('FRANCAIS', )
652
    get_navi_link = get_link_rel_next
653
    get_first_comic_link = simulate_first_link
654
    first_url = 'http://www.penelope-jolicoeur.com/2007/02/ma-vie-mon-oeuv.html'
655
656
    @classmethod
657
    def get_comic_info(cls, soup, link):
658
        """Get information about a particular comics."""
659
        date_str = soup.find('h2', class_='date-header').string
660
        day = string_to_date(date_str, "%A %d %B %Y", "fr_FR.utf8")
661
        imgs = soup.find('div', class_='entry-body').find_all('img')
662
        title = soup.find('h3', class_='entry-header').string
663
        return {
664
            'title': title,
665
            'img': [i['src'] for i in imgs],
666
            'month': day.month,
667
            'year': day.year,
668
            'day': day.day,
669
        }
670
671
672
class OneOneOneOneComic(GenericEmptyComic, GenericNavigableComic):
673
    """Class to retrieve 1111 Comics."""
@@ 1702-1722 (lines=21) @@
1699
1700
class WarehouseComic(GenericNavigableComic):
1701
    """Class to retrieve Warehouse Comic comics."""
1702
    name = 'warehouse'
1703
    long_name = 'Warehouse Comic'
1704
    url = 'http://warehousecomic.com'
1705
    get_first_comic_link = get_a_navi_navifirst
1706
    get_navi_link = get_link_rel_next
1707
1708
    @classmethod
1709
    def get_comic_info(cls, soup, link):
1710
        """Get information about a particular comics."""
1711
        title = soup.find('h2', class_='post-title').string
1712
        date_str = soup.find('span', class_='post-date').string
1713
        day = string_to_date(date_str, "%B %d, %Y")
1714
        imgs = soup.find('div', id='comic').find_all('img')
1715
        return {
1716
            'img': [i['src'] for i in imgs],
1717
            'title': title,
1718
            'day': day.day,
1719
            'month': day.month,
1720
            'year': day.year,
1721
        }
1722
1723
1724
class JustSayEh(GenericNavigableComic):
1725
    """Class to retrieve Just Say Eh comics."""
@@ 2583-2611 (lines=29) @@
2580
        return {
2581
            'img': [i['src'] for i in imgs],
2582
            'title': title,
2583
            'day': day.day,
2584
            'month': day.month,
2585
            'year': day.year
2586
        }
2587
2588
2589
class PleasantThoughts(GenericNavigableComic):
2590
    """Class to retrieve Pleasant Thoughts comics."""
2591
    name = 'pleasant'
2592
    long_name = 'Pleasant Thoughts'
2593
    url = 'http://pleasant-thoughts.com'
2594
    get_first_comic_link = get_a_navi_navifirst
2595
    get_navi_link = get_link_rel_next
2596
2597
    @classmethod
2598
    def get_comic_info(cls, soup, link):
2599
        """Get information about a particular comics."""
2600
        post = soup.find('div', class_='post-content')
2601
        title = post.find('h2', class_='post-title').string
2602
        imgs = post.find("div", class_="entry").find_all("img")
2603
        return {
2604
            'title': title,
2605
            'img': [i['src'] for i in imgs],
2606
        }
2607
2608
2609
class MisterAndMe(GenericNavigableComic):
2610
    """Class to retrieve Mister & Me Comics."""
2611
    # Also on http://www.gocomics.com/mister-and-me
2612
    # Also on https://tapastic.com/series/Mister-and-Me
2613
    name = 'mister'
2614
    long_name = 'Mister & Me'
@@ 360-382 (lines=23) @@
357
        return []
358
359
360
class ExtraFabulousComics(GenericNavigableComic):
361
    """Class to retrieve Extra Fabulous Comics."""
362
    name = 'efc'
363
    long_name = 'Extra Fabulous Comics'
364
    url = 'http://extrafabulouscomics.com'
365
    get_first_comic_link = get_a_navi_navifirst
366
    get_navi_link = get_link_rel_next
367
368
    @classmethod
369
    def get_comic_info(cls, soup, link):
370
        """Get information about a particular comics."""
371
        img_src_re = re.compile('^%s/wp-content/uploads/' % cls.url)
372
        imgs = soup.find_all('img', src=img_src_re)
373
        title = soup.find('meta', property='og:title')['content']
374
        date_str = soup.find('meta', property='article:published_time')['content'][:10]
375
        day = string_to_date(date_str, "%Y-%m-%d")
376
        return {
377
            'title': title,
378
            'img': [i['src'] for i in imgs],
379
            'month': day.month,
380
            'year': day.year,
381
            'day': day.day,
382
            'prefix': title + '-'
383
        }
384
385