Package: pychan
pychanOverviewInstallationUsageGeneral NotesSetupFetch Board NamesFetch ThreadsFetch Archived ThreadsSearch 4chanAbout CloudflareSearch 4chan Code ExampleFetch Posts for a Specific Threadpychan ModelsThreadsPostsA Note About RepliesPostersFilesContributingOverviewpychanis a Python client for interacting with 4chan. 4chan does not have an official API, and
attempts to implement one by third parties have tended to languish, so instead, this library
provides abstractions over interacting with (scraping) 4chan directly.pychanis object-oriented
and its implementation is lazy where reasonable (using Python Generators) in order to optimize
performance and minimize superfluous blocking I/O operations.InstallationIf you have Python >=3.10 and <4.0 installed,pychancan be installed from PyPI using
something likepipinstallpychanUsageGeneral NotesAll 4chan interactions are throttled internally by sleeping the executing thread. If you executepychanin a multithreaded way, you will not get the benefits of this throttling.pychandoes not
take responsibility for the consequences of excessive HTTP requests in such cases.SetupfrompychanimportFourChan,LogLevel,PychanLogger# With all defaults (logging disabled, all exceptions raised)fourchan=FourChan()# Tell pychan to gracefully ignore HTTP exceptions, if any, within its internal logicfourchan=FourChan(raise_http_exceptions=False)# Tell pychan to gracefully ignore parsing exceptions, if any, within its internal logicfourchan=FourChan(raise_parsing_exceptions=False)# Configure logging explicitlylogger=PychanLogger(LogLevel.INFO)fourchan=FourChan(logger=logger)# Use all of the above settings at oncelogger=PychanLogger(LogLevel.INFO)fourchan=FourChan(logger=logger,raise_http_exceptions=True,raise_parsing_exceptions=True)The rest of the examples in thisREADMEassume that you have already created an instance of theFourChanclass as shown above.Fetch Board NamesThis function dynamically fetches boards from 4chan at call time.Note: boards which are not compatible withpychanare not returned in this list.boards=fourchan.get_boards()# Sample return value:# ['a', 'b', 'c', 'd', 'e', 'g', 'gif', 'h', 'hr', 'k', 'm', 'o', 'p', 'r', 's', 't', 'u', 'v', 'vg', 'vm', 'vmg', 'vr', 'vrpg', 'vst', 'w', 'wg', 'i', 'ic', 'r9k', 's4s', 'vip', 'qa', 'cm', 'hm', 'lgbt', 'y', '3', 'aco', 'adv', 'an', 'bant', 'biz', 'cgl', 'ck', 'co', 'diy', 'fa', 'fit', 'gd', 'hc', 'his', 'int', 'jp', 'lit', 'mlp', 'mu', 'n', 'news', 'out', 'po', 'pol', 'pw', 'qst', 'sci', 'soc', 'sp', 'tg', 'toy', 'trv', 'tv', 'vp', 'vt', 'wsg', 'wsr', 'x', 'xs']Fetch Threads# Iterate over all threads in /b/forthreadinfourchan.get_threads("b"):# Do stuff with the threadprint(thread.title)# You can also iterate over all the posts in the threadforpostinfourchan.get_posts(thread):# Do stuff with the post - refer to the model documentation in pychan's README for detailsprint(post.text)Fetch Archived ThreadsNote: some boards do not have an archive (e.g./b/). Such boards will either return an empty list
or raise an exception depending on how you have configured yourFourChaninstance.The threads returned by this function will always have atitlefield containing the text shown in
4chan's interface under the "Excerpt" column header. This text can be either the thread's real title
or a preview of the original post's text. Passing any of the threads returned by this method to theget_posts()method will automatically correct thetitlefield (if necessary) on the thread that
gets attached to the returned posts. SeeFetch Posts for a Specific Threadfor more details.Technically,pychancould address thetitlebehavior described above by issuing an additional
HTTP request for each thread to get its real title, but in the spirit of making the smallest number
of HTTP requests possible,pychandirectly uses the excerpt instead.forthreadinfourchan.get_archived_threads("pol"):# Do stuff with the threadprint(thread.title)# You can also iterate over all the posts in the threadforpostinfourchan.get_posts(thread):# Do stuff with the post - refer to the model documentation in pychan's README for detailsprint(post.text)Search 4chanAbout CloudflarePerforming searches against 4chan is much more cumbersome than accessing the rest of 4chan's data.
This is because 4chan has a Cloudflare firewall in front of its REST API, so the only way to get
data back from searches is to supply the HTTP request information needed to bypass Cloudflare's
anti-bot checks. Ultimately, this amounts to passing certain headers along with the HTTP request,
but the challenge comes from actually acquiring such headers.It is currently beyond the scope ofpychanto generate these headers for you, so if you would like
to automate the circumvention of Cloudflare's protections, you may want to look into using a project
like one of the following (this list is alphabetized and not exhaustive):ultrafunkamsterdam/undetected-chromedriverVeNoMouS/cloudscraperwkeeling/selenium-wireA manual way to acquire these values is to perform a 4chan search using a web browser and leverage
the browser's Developer Tools to trace the network requests that were made during the search. The
request that contains the Cloudflare values will have been made tohttps://find.4chan.org/apiwith
some query parameters. Once you have found this request, copy theUser-AgentandCookievalues
that were sent in your request, then pass them topychan'ssearch()method. Be aware that the
Cloudflare cookie(s) have an expiration on them, so this manual workaround will only return results
until Cloudflare invalidates your cookie(s). After that, you will need to acquire new values.Search 4chan Code ExampleNote: closed/stickied/archived threads are never returned in search results.# This "threads" variable will contain a Python Generator (not a list) in order to facilitate lazinessthreads=fourchan.search(board="b",text="ylyl",user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",cloudflare_cookies={"cf_clearance":"bm2RICpcDeR4cXoC2nfI_cnZcbAkN4UYpN6c1zzeb8g-1440859602-0-160"})forthreadinthreads:# The thread object is the same class as the one returned by get_threads()forpostinfourchan.get_posts(thread):# Do stuff with the post - refer to the model documentation in pychan's README for detailsprint(post.text)Fetch Posts for a Specific Threadfrompychan.modelsimportThread# Instantiate a Thread instance with which to query for poststhread=Thread("int",168484869)# Note: the thread contained within the returned posts will have all applicable metadata (such as# title and sticky status), regardless of whether you provided such data above - pychan will# "auto-discover" all metadata and include it in the post models' copy of the threadposts=fourchan.get_posts(thread)pychan ModelsThe following tables summarize all the kinds of data that are available on the various models used
by this library.Also note that all model classes inpychanimplement the following methods:__repr____str____hash____eq____iter__- this is implemented so that the models may be passed to Python'stuple()function__copy____deepcopy__ThreadsThe table below corresponds to thepychan.models.Threadclass.FieldTypeExample Value(s)thread.boardstr"b","int"thread.numberint882774935,168484869thread.titleOptional[str]None,"YLYL thread"thread.is_stickiedboolTrue,Falsethread.is_closedboolTrue,Falsethread.is_archivedboolTrue,Falsethread.urlstr"https://boards.4chan.org/a/thread/251097344"PostsThe table below corresponds to thepychan.models.Postclass.FieldTypeExample Value(s)post.threadThreadpychan.models.Threadpost.numberint882774935,882774974post.timestampdatetime.datetimedatetime.datetimepost.posterPosterpychan.models.Posterpost.textstr">be me\n>be bored\n>write pychan\n>somehow it works"post.is_original_postboolTrue,Falsepost.fileOptional[File]None,pychan.models.Filepost.replieslist[Post][],[pychan.models.Post, pychan.models.Post]post.urlstr"https://boards.4chan.org/a/thread/251097344#p251097419"A Note About RepliesTherepliesfield shown above is purely a convenience featurepychanprovides for accessing all
posts within a thread which used the>>operator to "reply" to the current post. When you call theget_posts()method, you will still receive every available post and reply (in the order they were
all posted) as a single, flat list.PostersThe table below corresponds to thepychan.models.Posterclass.FieldTypeExample Value(s)poster.namestr"Anonymous"poster.is_moderatorboolTrue,Falseposter.idOptional[str]None,"BYagKQXI"poster.flagOptional[str]None,"United States","Canada"FilesThe table below corresponds to thepychan.models.Fileclass.FieldTypeExample Value(s)file.urlstr"https://i.4cdn.org/pol/1658892700380132.jpg"file.namestr"wojak.jpg","i feel alone.jpg"file.sizestr"601 KB"file.dimensionstuple[int, int](1920, 1080),(800, 600)file.is_spoilerboolTrue,FalseContributingSeeCONTRIBUTING.mdfor developer-oriented information.